Floating rules are now isolated between workspaces

This commit is contained in:
2025-06-11 11:48:31 +02:00
parent d9d2a74f02
commit b400cb539f
8 changed files with 176 additions and 16 deletions

View File

@@ -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})

View File

@@ -1,13 +1,17 @@
#ifndef FILE_SERVICES_H
#define FILE_SERVICES_H
#ifndef FILE_SERVICE_H
#define FILE_SERVICE_H
#include <string>
#include <vector>
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<std::string> readLines(std::string);
};
#endif

View File

@@ -4,13 +4,17 @@
#include <list>
#include "Workspace.h"
#include "Client.h"
#include "WindowRule.h"
class HyprlandService {
public:
static Workspace getCurrentWorkspace();
static std::list<Client> getClients();
static std::list<Client> getClientsOnActiveWorkspace();
static std::list<WindowRule> getWindowRules();
static std::optional<WindowRule> findConflictingRule(WindowRule);
static void setFloatingRule(bool);
static void removeRule(WindowRule);
static void setClientFloating(Client&);
static void setClientTiled(Client&);
static void toggleClientFloating(Client&);

View File

@@ -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

20
include/WindowRule.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef WINDOW_RULE_H
#define WINDOW_RULE_H
#include <string>
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

View File

@@ -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<std::string> lines = readLines(path);
if (n < 0 || n >= static_cast<int>(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<std::string> FileService::readLines(std::string path) {
std::vector<std::string> lines;
std::ifstream file(path);
std::string line;
while (std::getline(file, line)) {
lines.push_back(line);
}
return lines;
};

View File

@@ -17,8 +17,8 @@ std::list<Client> HyprlandService::getClients() {
};
std::list<Client> HyprlandService::getClientsOnActiveWorkspace() {
std::list<Client> clients = HyprlandService::getClients();
int activeWorkspaceID = HyprlandService::getCurrentWorkspace().id;
std::list<Client> 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<Client> HyprlandService::getClientsOnActiveWorkspace() {
return clients;
};
std::list<WindowRule> HyprlandService::getWindowRules() {
std::list<WindowRule> 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<WindowRule> HyprlandService::findConflictingRule(WindowRule subject) {
std::list<WindowRule> 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<WindowRule> 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<WindowRule> rules = getWindowRules();
int id = getCurrentWorkspace().id;
for (auto& rule : rules) {
if (rule.workspaceID == id && rule.tile == false) {
return true;
}
}
return false;
};

31
src/WindowRule.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include <algorithm>
#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;
}