diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f18be0..40a83dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable(htt src/Workspace.cpp src/ShellService.cpp src/FileService.cpp + src/WindowRule.cpp src/Client.cpp) target_link_libraries(htt PRIVATE nlohmann_json::nlohmann_json) target_include_directories(htt PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/include/FileService.h b/include/FileService.h index b59aa6b..a5c963a 100644 --- a/include/FileService.h +++ b/include/FileService.h @@ -1,13 +1,17 @@ -#ifndef FILE_SERVICES_H -#define FILE_SERVICES_H +#ifndef FILE_SERVICE_H +#define FILE_SERVICE_H #include +#include -class FileServices { +class FileService { public: static bool doesNonEmptyFileExist(std::string); static void emptyFile(std::string); static void createOrOverwriteFile(std::string, std::string); + static void appendToFile(std::string, std::string); + static void deleteNthLine(std::string, int); + static std::vector readLines(std::string); }; #endif \ No newline at end of file diff --git a/include/HyprlandService.h b/include/HyprlandService.h index 39d01bd..6c48e4d 100644 --- a/include/HyprlandService.h +++ b/include/HyprlandService.h @@ -4,13 +4,17 @@ #include #include "Workspace.h" #include "Client.h" +#include "WindowRule.h" class HyprlandService { public: static Workspace getCurrentWorkspace(); static std::list getClients(); static std::list getClientsOnActiveWorkspace(); + static std::list getWindowRules(); + static std::optional findConflictingRule(WindowRule); static void setFloatingRule(bool); + static void removeRule(WindowRule); static void setClientFloating(Client&); static void setClientTiled(Client&); static void toggleClientFloating(Client&); diff --git a/include/Macros.h b/include/Macros.h index 923d71e..c53623d 100644 --- a/include/Macros.h +++ b/include/Macros.h @@ -9,6 +9,6 @@ #define ECHO_PATH "/usr/bin/echo" #define CAT_PATH "/usr/bin/cat" -#define FLOATING_RULE "windowrule = float,class:.*" +#define FLOATING_RULE "windowrule = float, onworkspace:" #endif \ No newline at end of file diff --git a/include/WindowRule.h b/include/WindowRule.h new file mode 100644 index 0000000..b3c206f --- /dev/null +++ b/include/WindowRule.h @@ -0,0 +1,20 @@ +#ifndef WINDOW_RULE_H +#define WINDOW_RULE_H + +#include + +class WindowRule { + public: + //true -> tiling mode + //false -> floating mode + bool tile; + + //Which workspace to apply to + int workspaceID; + + std::string toString(); + + static WindowRule parse(std::string); +}; + +#endif \ No newline at end of file diff --git a/src/FileService.cpp b/src/FileService.cpp index fb89e04..b4bf937 100644 --- a/src/FileService.cpp +++ b/src/FileService.cpp @@ -4,19 +4,55 @@ #include "../include/Macros.h" #include "../include/ShellService.h" -bool FileServices::doesNonEmptyFileExist(std::string path) { +bool FileService::doesNonEmptyFileExist(std::string path) { std::ifstream file(path); return file.good() && file.peek() != std::ifstream::traits_type::eof(); }; -void FileServices::emptyFile(std::string path) { +void FileService::emptyFile(std::string path) { std::stringstream cmd; cmd << CAT_PATH << " " << NULL_PATH << " > " << path; ShellService::exec(cmd.str().c_str()); }; -void FileServices::createOrOverwriteFile(std::string path, std::string content) { +void FileService::createOrOverwriteFile(std::string path, std::string content) { std::stringstream cmd; cmd << ECHO_PATH << " " << content << " > " << path; ShellService::exec(cmd.str().c_str()); +}; + +void FileService::appendToFile(std::string path, std::string content) { + std::stringstream cmd; + cmd << ECHO_PATH << " \"" << "\n" << content << "\" >> " << path; + ShellService::exec(cmd.str().c_str()); +}; + +void FileService::deleteNthLine(std::string path, int n) { + std::vector lines = readLines(path); + + if (n < 0 || n >= static_cast(lines.size())) { + // Out of bounds, do nothing + return; + } + + lines.erase(lines.begin() + n); + + std::ofstream file(path, std::ios::trunc); + for (size_t i = 0; i < lines.size(); ++i) { + file << lines[i]; + if (i != lines.size() - 1) { + file << '\n'; + } + } +} + +std::vector FileService::readLines(std::string path) { + std::vector lines; + std::ifstream file(path); + std::string line; + + while (std::getline(file, line)) { + lines.push_back(line); + } + return lines; }; \ No newline at end of file diff --git a/src/HyprlandService.cpp b/src/HyprlandService.cpp index 52dd0e2..e3d62e4 100644 --- a/src/HyprlandService.cpp +++ b/src/HyprlandService.cpp @@ -17,8 +17,8 @@ std::list HyprlandService::getClients() { }; std::list HyprlandService::getClientsOnActiveWorkspace() { - std::list clients = HyprlandService::getClients(); - int activeWorkspaceID = HyprlandService::getCurrentWorkspace().id; + std::list clients = getClients(); + int activeWorkspaceID = getCurrentWorkspace().id; for (auto it = clients.begin(); it != clients.end(); ) { if (it->workspace.id != activeWorkspaceID) { @@ -31,6 +31,16 @@ std::list HyprlandService::getClientsOnActiveWorkspace() { return clients; }; +std::list HyprlandService::getWindowRules() { + std::list rules; + + for (auto& line : FileService::readLines(ShellService::getHomePath()+FLOATING_RULE_CONF_FILE)) { + rules.push_back(WindowRule::parse(line)); + } + + return rules; +}; + void HyprlandService::setClientFloating(Client& c) { ShellService::exec(HYPRCTL_BINARY " dispatch setfloating address:" + c.address); }; @@ -43,15 +53,69 @@ void HyprlandService::toggleClientFloating(Client& c) { ShellService::exec(HYPRCTL_BINARY " dispatch togglefloating address:" + c.address); }; -void HyprlandService::setFloatingRule(bool b) { - if (b) { - FileServices::createOrOverwriteFile(ShellService::getHomePath()+FLOATING_RULE_CONF_FILE, FLOATING_RULE); - } - else { - FileServices::emptyFile(ShellService::getHomePath()+FLOATING_RULE_CONF_FILE); +//on = true -> creates a window rule to ENABLE floating mode for currently active workspace +//on = false -> creates a window rule to DISABLE floating mode for currently active workspace +void HyprlandService::setFloatingRule(bool on) { + WindowRule rule {.tile = !on, .workspaceID = getCurrentWorkspace().id}; + auto conflictingRule = findConflictingRule(rule); + if (conflictingRule.has_value()) { + removeRule(conflictingRule.value()); } + + FileService::appendToFile( + ShellService::getHomePath() + FLOATING_RULE_CONF_FILE, + rule.toString() + ); }; +std::optional HyprlandService::findConflictingRule(WindowRule subject) { + std::list rules = getWindowRules(); + int id = getCurrentWorkspace().id; + + for (auto& rule : rules) { + if (rule.tile == !subject.tile && rule.workspaceID == subject.workspaceID) + { + return rule; + } + } + + return std::nullopt; +}; + +void HyprlandService::removeRule(WindowRule rule) { + std::list rules = getWindowRules(); + int index = 0; + int foundIndex = -1; + + for (auto& it : rules) { + if (it.toString() == rule.toString()) + { + foundIndex = index; + break; + } + ++index; + } + + if (foundIndex != -1) { + FileService::deleteNthLine( + ShellService::getHomePath() + FLOATING_RULE_CONF_FILE, + foundIndex + ); + } + + //else: rule not found, do nothing +} + bool HyprlandService::isFloatingRulePresent() { - return FileServices::doesNonEmptyFileExist(ShellService::getHomePath()+FLOATING_RULE_CONF_FILE); + //Checks if there's a valid window rule in place that enables floating mode for the currently active workspace + std::list rules = getWindowRules(); + int id = getCurrentWorkspace().id; + + for (auto& rule : rules) { + if (rule.workspaceID == id && rule.tile == false) { + return true; + } + } + + return false; }; \ No newline at end of file diff --git a/src/WindowRule.cpp b/src/WindowRule.cpp new file mode 100644 index 0000000..aedaac7 --- /dev/null +++ b/src/WindowRule.cpp @@ -0,0 +1,31 @@ +#include +#include "../include/WindowRule.h" + +std::string WindowRule::toString() { + std::string mode = this->tile ? "tile" : "float"; + + return "windowrule = " + mode + ", onworkspace:" + std::to_string(this->workspaceID); +}; + +WindowRule WindowRule::parse(std::string ruleStr) { + WindowRule rule; + // Remove spaces for easier parsing + ruleStr.erase(std::remove(ruleStr.begin(), ruleStr.end(), ' '), ruleStr.end()); + + // Find mode + auto modePos = ruleStr.find("windowrule="); + if (modePos != std::string::npos) { + auto commaPos = ruleStr.find(',', modePos); + std::string mode = ruleStr.substr(modePos + 11, commaPos - (modePos + 11)); + rule.tile = (mode == "tile"); + } + + // Find workspace + auto wsPos = ruleStr.find("onworkspace:"); + if (wsPos != std::string::npos) { + std::string wsStr = ruleStr.substr(wsPos + 12); + rule.workspaceID = std::stoi(wsStr); + } + + return rule; +} \ No newline at end of file