12 Commits
v1.2 ... v1.4.2

22 changed files with 410 additions and 117 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.cache/ .cache/
build/ build/
.vscode/ .vscode/
result

View File

@@ -1,9 +1,45 @@
cmake_minimum_required(VERSION 3.10.0) cmake_minimum_required(VERSION 3.16)
project(hyprland-toggle-tiling VERSION 0.1.0 LANGUAGES C CXX)
project(hyprland-toggle-tiling
VERSION 1.4.2
LANGUAGES CXX
)
include(GNUInstallDirs)
include(FetchContent) include(FetchContent)
FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz)
FetchContent_MakeAvailable(json) # --------------------------------------------------
# Dependency handling
# --------------------------------------------------
option(USE_SYSTEM_JSON
"Use system-installed nlohmann_json instead of fetching"
OFF
)
if(USE_SYSTEM_JSON)
find_package(nlohmann_json CONFIG REQUIRED)
else()
# Try system package first silently
find_package(nlohmann_json CONFIG QUIET)
if(NOT nlohmann_json_FOUND)
message(STATUS "nlohmann_json not found, fetching from GitHub")
FetchContent_Declare(
nlohmann_json
URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz
)
FetchContent_MakeAvailable(nlohmann_json)
else()
message(STATUS "Using system nlohmann_json")
endif()
endif()
# --------------------------------------------------
# Executable
# --------------------------------------------------
add_executable(htt add_executable(htt
src/main.cpp src/main.cpp
@@ -12,6 +48,22 @@ add_executable(htt
src/ShellService.cpp src/ShellService.cpp
src/FileService.cpp src/FileService.cpp
src/WindowRule.cpp src/WindowRule.cpp
src/Client.cpp) src/Client.cpp
target_link_libraries(htt PRIVATE nlohmann_json::nlohmann_json) )
target_include_directories(htt PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_features(htt PRIVATE cxx_std_17)
target_link_libraries(htt
PRIVATE
nlohmann_json::nlohmann_json
)
target_include_directories(htt PRIVATE ${PROJECT_SOURCE_DIR}/src)
# --------------------------------------------------
# Install
# --------------------------------------------------
install(TARGETS htt
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

View File

@@ -29,6 +29,8 @@ Check out [the demo](https://typofelho.ddns.net/TypoMustakes/hyprland-toggle-til
Let's say you switched to tiling mode. From that point on, new windows will open in tiling mode as well. Let's say you switched to tiling mode. From that point on, new windows will open in tiling mode as well.
Switch to floating mode again and newly opened windows will be in floating mode. Switch to floating mode again and newly opened windows will be in floating mode.
- Floating/tiling window rules are isolated between workspaces. You can set one workspace to be floating, and all the rest to tiling for example. - Floating/tiling window rules are isolated between workspaces. You can set one workspace to be floating, and all the rest to tiling for example.
- Returns applied rule for the current workspace. Useful for scripting.
- Moving a window from a tiling workspace to a floating one changes the float state of the window to respect the new workspace.
- Useful for workflows that require both tiling and floating window management. - Useful for workflows that require both tiling and floating window management.
- Lightweight and easy to integrate with your Hyprland setup. - Lightweight and easy to integrate with your Hyprland setup.
@@ -40,9 +42,37 @@ You have a few options of obtainting the program.
You may install this program [from the AUR.](https://aur.archlinux.org/packages/hyprland-toggle-tiling-git) You may install this program [from the AUR.](https://aur.archlinux.org/packages/hyprland-toggle-tiling-git)
### Nix Flake (for Nix users)
This project provides a Nix flake for reproducible builds and easy installation.
#### Build the package
```shell
nix build git+ssh://gitea@typofelho.ddns.net/TypoMustakes/hyprland-toggle-tiling.git
```
The binary will be at `./result/bin/htt`.
#### Add to Home Manager
1. Add the flake as an input:
```shell
inputs.htt.url = "git+ssh://gitea@typofelho.ddns.net/TypoMustakes/hyprland-toggle-tiling.git";
```
2. Then include it in your configuration:
```shell
home.packages = [
inputs.htt.packages.${pkgs.system}.default
];
```
### Download the release ### Download the release
You can also [download the release binary.](https://typofelho.ddns.net/TypoMustakes/hyprland-toggle-tiling/releases/tag/1.1.0) You can also [download the release binary.](https://typofelho.ddns.net/TypoMustakes/hyprland-toggle-tiling/releases)
or go nuts and... or go nuts and...
@@ -75,8 +105,21 @@ or go nuts and...
# Usage # Usage
```shell ```shell
./htt <config-file-path> ./htt <config-file-path> (flags)
``` ```
- `-q`: Print the rule applied on the current workspace to STDOUT, but don't change anything. If there isn't an active rule for the current workspace, it returns a tiling rule. The returned string is a valid Hyprland window rule configuration line, like so:
```shell
# (On workspace 2...)
$ ./htt ~/.cache/htt/rules -q
windowrule = tile on, match:workspace 2
```
Potential applications for this are mainly scripts, like my waybar module here:
![wayland module showcase](https://typofelho.ddns.net/TypoMustakes/hyprland-toggle-tiling/raw/branch/master/assets/waybar_module.gif)
- `-m [integer]`: Move currently active window to specified workspace. Upon moving, the window will adapt to the windowing mode of the new workspace. Doesn't work with `-q`. See ![the 1.4 changelog](https://typofelho.ddns.net/TypoMustakes/hyprland-toggle-tiling/releases/tag/v1.4) for details.
- If the specified configuration file does not exist, it will be created. - If the specified configuration file does not exist, it will be created.
- If the configuration contains existing rules, this should still work, but your existing configuration will probably get a bit messy, syntax-wise. I advise against it. - If the configuration contains existing rules, this should still work, but your existing configuration will probably get a bit messy, syntax-wise. I advise against it.
@@ -92,5 +135,37 @@ or go nuts and...
bind = $mod + t, exec, /path/to/htt <config-file-path> bind = $mod + t, exec, /path/to/htt <config-file-path>
``` ```
Or not. Do whatever you want. 3. If you want windows to respect workspaces' rules after moving them to another workspace (see ![the 1.4 changelog](https://typofelho.ddns.net/TypoMustakes/hyprland-toggle-tiling/releases/tag/v1.4) for details), you might want to tell HTT to move your windows instead of telling Hyprland.
On the default configuration, this would mean changing this:
```sh
bind = $mainMod SHIFT, 1, movetoworkspace, 1
bind = $mainMod SHIFT, 2, movetoworkspace, 2
bind = $mainMod SHIFT, 3, movetoworkspace, 3
bind = $mainMod SHIFT, 4, movetoworkspace, 4
bind = $mainMod SHIFT, 5, movetoworkspace, 5
bind = $mainMod SHIFT, 6, movetoworkspace, 6
bind = $mainMod SHIFT, 7, movetoworkspace, 7
bind = $mainMod SHIFT, 8, movetoworkspace, 8
bind = $mainMod SHIFT, 9, movetoworkspace, 9
bind = $mainMod SHIFT, 0, movetoworkspace, 10
```
to this:
```sh
bind = $mainMod SHIFT, 1, exec, htt [your htt config file] -m 1
bind = $mainMod SHIFT, 2, exec, htt [your htt config file] -m 2
bind = $mainMod SHIFT, 3, exec, htt [your htt config file] -m 3
bind = $mainMod SHIFT, 4, exec, htt [your htt config file] -m 4
bind = $mainMod SHIFT, 5, exec, htt [your htt config file] -m 5
bind = $mainMod SHIFT, 6, exec, htt [your htt config file] -m 6
bind = $mainMod SHIFT, 7, exec, htt [your htt config file] -m 7
bind = $mainMod SHIFT, 8, exec, htt [your htt config file] -m 8
bind = $mainMod SHIFT, 9, exec, htt [your htt config file] -m 9
bind = $mainMod SHIFT, 0, exec, htt [your htt config file] -m 10
```
... in your configuration.
Otherwise, just use `exec, htt [your htt config file] -m [workspace ID]` wherever you used `movetoworkspace [workspace ID]` before.

BIN
assets/waybar_module.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

37
default.nix Normal file
View File

@@ -0,0 +1,37 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.stdenv.mkDerivation rec {
pname = "hyprland-toggle-tiling";
version = "1.4.2";
src = pkgs.fetchFromGitHub {
owner = "TypoMustakes";
repo = "hyprland-toggle-tiling";
rev = "v${version}";
sha256 = "sha256-NxNT2pZ4zWkF5JPPUcqJl+VFOIHyxq4ZsACg9GsWnwM=";
};
nativeBuildInputs = [
pkgs.cmake
pkgs.pkg-config
];
buildInputs = [
pkgs.hyprland
pkgs.nlohmann_json
];
cmakeFlags = [
"-DUSE_SYSTEM_JSON=ON"
];
cmakeBuildType = "Release";
meta = with pkgs.lib; {
description = "Toggle tiling and floating modes in Hyprland globally.";
homepage = "https://github.com/TypoMustakes/hyprland-toggle-tiling";
license = licenses.gpl3Only;
platforms = platforms.linux;
mainProgram = "htt";
};
}

27
flake.lock generated Normal file
View File

@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1775710090,
"narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "4c1018dae018162ec878d42fec712642d214fdfa",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

20
flake.nix Normal file
View File

@@ -0,0 +1,20 @@
{
description = "hyprland-toggle-tiling";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in
{
packages.${system}.default = pkgs.callPackage ./default.nix {};
apps.${system}.default = {
type = "app";
program = "${self.packages.${system}.default}/bin/htt";
};
};
}

View File

@@ -23,7 +23,6 @@ class Client {
std::array<int, 2> size; std::array<int, 2> size;
WorkspaceSignature workspace; WorkspaceSignature workspace;
bool floating; bool floating;
bool pseudo;
int monitor; int monitor;
std::string className; std::string className;
std::string title; std::string title;
@@ -34,6 +33,7 @@ class Client {
bool pinned; bool pinned;
int fullscreen; int fullscreen;
int fullscreenClient; int fullscreenClient;
bool overFullscreen;
std::string swallowing; std::string swallowing;
int focusHistory; int focusHistory;
bool inhibitingIdle; bool inhibitingIdle;

View File

@@ -2,26 +2,36 @@
#define HYPRLAND_SERVICE_H #define HYPRLAND_SERVICE_H
#include <list> #include <list>
#include "Workspace.h" #include "Workspace.hpp"
#include "Client.h" #include "Client.hpp"
#include "WindowRule.h" #include "WindowRule.hpp"
class HyprlandService { class HyprlandService {
private: private:
static std::string configFilePath; static std::string configFilePath;
public:
static void setConfigFilePath(std::string);
static std::string getConfigFilePath(); static std::string getConfigFilePath();
static std::list<Workspace> getWorkspaces();
static std::optional<Workspace> getWorkspace(int);
static Workspace getCurrentWorkspace(); static Workspace getCurrentWorkspace();
static std::list<Client> getClients(); static std::list<Client> getClients();
static std::list<Client> getClientsOnActiveWorkspace(); static std::list<Client> getClientsOnActiveWorkspace();
static Client getActiveClient();
static bool isFloatingRulePresent(int);
static bool isFloatingRulePresent(Workspace);
static std::list<WindowRule> getWindowRules(); static std::list<WindowRule> getWindowRules();
static std::optional<WindowRule> findConflictingRule(WindowRule); static std::optional<WindowRule> findConflictingRule(WindowRule);
static void setFloatingRule(bool); static void setFloatingRule(bool);
static void removeRule(WindowRule); static void removeRule(WindowRule);
static void setClientFloating(Client&);
static void setClientTiled(Client&); static void setClientFloating(Client);
static void toggleClientFloating(Client&); static void setClientTiled(Client);
static bool isFloatingRulePresent(); public:
static void toggleFloating();
static void moveToWorkspace(int);
static void setConfigFilePath(std::string);
static WindowRule getActiveWorkspaceRule();
}; };
#endif #endif

View File

@@ -1,9 +0,0 @@
#ifndef MACROS_H
#define MACROS_H
#define HYPRCTL_BINARY "/usr/bin/hyprctl"
#define NULL_PATH "/dev/null"
#define ECHO_PATH "/usr/bin/echo"
#define CAT_PATH "/usr/bin/cat"
#endif

9
include/Macros.hpp Normal file
View File

@@ -0,0 +1,9 @@
#ifndef MACROS_H
#define MACROS_H
#define HYPRCTL_BINARY "hyprctl"
#define NULL_PATH "/dev/null"
#define ECHO_PATH "echo"
#define CAT_PATH "cat"
#endif

View File

@@ -1,4 +1,4 @@
#include "../include/Client.h" #include "../include/Client.hpp"
void from_json(const json& j, WorkspaceSignature& ws) { void from_json(const json& j, WorkspaceSignature& ws) {
j.at("id").get_to(ws.id); j.at("id").get_to(ws.id);
@@ -12,7 +12,6 @@ void from_json(const json& j, Client& client) {
j.at("size").get_to(client.size); j.at("size").get_to(client.size);
j.at("workspace").get_to(client.workspace); j.at("workspace").get_to(client.workspace);
j.at("floating").get_to(client.floating); j.at("floating").get_to(client.floating);
j.at("pseudo").get_to(client.pseudo);
j.at("monitor").get_to(client.monitor); j.at("monitor").get_to(client.monitor);
j.at("class").get_to(client.className); // Maps "class" JSON field to className j.at("class").get_to(client.className); // Maps "class" JSON field to className
j.at("title").get_to(client.title); j.at("title").get_to(client.title);
@@ -23,6 +22,7 @@ void from_json(const json& j, Client& client) {
j.at("pinned").get_to(client.pinned); j.at("pinned").get_to(client.pinned);
j.at("fullscreen").get_to(client.fullscreen); j.at("fullscreen").get_to(client.fullscreen);
j.at("fullscreenClient").get_to(client.fullscreenClient); j.at("fullscreenClient").get_to(client.fullscreenClient);
j.at("overFullscreen").get_to(client.overFullscreen);
j.at("swallowing").get_to(client.swallowing); j.at("swallowing").get_to(client.swallowing);
j.at("focusHistoryID").get_to(client.focusHistory); j.at("focusHistoryID").get_to(client.focusHistory);
j.at("inhibitingIdle").get_to(client.inhibitingIdle); j.at("inhibitingIdle").get_to(client.inhibitingIdle);

View File

@@ -1,8 +1,8 @@
#include <sstream> #include <sstream>
#include <fstream> #include <fstream>
#include "../include/FileService.h" #include "../include/FileService.hpp"
#include "../include/Macros.h" #include "../include/Macros.hpp"
#include "../include/ShellService.h" #include "../include/ShellService.hpp"
bool FileService::doesNonEmptyFileExist(std::string path) { bool FileService::doesNonEmptyFileExist(std::string path) {
std::ifstream file(path); std::ifstream file(path);

View File

@@ -1,15 +1,17 @@
#include "../include/HyprlandService.hpp"
#include "../include/FileService.hpp"
#include "../include/Macros.hpp"
#include "../include/ShellService.hpp"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include "../include/HyprlandService.h" #include <optional>
#include "../include/ShellService.h" #include <string>
#include "../include/Macros.h"
#include "../include/FileService.h"
using json = nlohmann::json; using json = nlohmann::json;
std::string HyprlandService::configFilePath; std::string HyprlandService::configFilePath;
void HyprlandService::setConfigFilePath(std::string path) { void HyprlandService::setConfigFilePath(std::string path) {
//Look for substring "~/". If found, expand it // Look for substring "~/". If found, expand it
const std::string tilde = "~/"; const std::string tilde = "~/";
size_t pos = path.find(tilde); size_t pos = path.find(tilde);
if (pos != std::string::npos) { if (pos != std::string::npos) {
@@ -18,15 +20,38 @@ void HyprlandService::setConfigFilePath(std::string path) {
configFilePath = path; configFilePath = path;
}; };
std::string HyprlandService::getConfigFilePath() { std::string HyprlandService::getConfigFilePath() { return configFilePath; };
return configFilePath;
}; std::list<Workspace> HyprlandService::getWorkspaces() {
json j = json::parse(ShellService::exec(HYPRCTL_BINARY " workspaces -j"));
return j.get<std::list<Workspace>>();
}
std::optional<Workspace> HyprlandService::getWorkspace(int id) {
std::list<Workspace> workspaces = getWorkspaces();
for (auto it = workspaces.begin(); it != workspaces.end();) {
if (it->id == id) {
return *it;
} else {
++it;
}
}
return std::nullopt;
}
Workspace HyprlandService::getCurrentWorkspace() { Workspace HyprlandService::getCurrentWorkspace() {
json j = json::parse(ShellService::exec(HYPRCTL_BINARY " activeworkspace -j")); json j =
json::parse(ShellService::exec(HYPRCTL_BINARY " activeworkspace -j"));
return j.get<Workspace>(); return j.get<Workspace>();
}; };
Client HyprlandService::getActiveClient() {
json j = json::parse(ShellService::exec(HYPRCTL_BINARY " activewindow -j"));
return j.get<Client>();
}
std::list<Client> HyprlandService::getClients() { std::list<Client> HyprlandService::getClients() {
json j = json::parse(ShellService::exec(HYPRCTL_BINARY " clients -j")); json j = json::parse(ShellService::exec(HYPRCTL_BINARY " clients -j"));
return j.get<std::list<Client>>(); return j.get<std::list<Client>>();
@@ -36,7 +61,7 @@ std::list<Client> HyprlandService::getClientsOnActiveWorkspace() {
std::list<Client> clients = getClients(); std::list<Client> clients = getClients();
int activeWorkspaceID = getCurrentWorkspace().id; int activeWorkspaceID = getCurrentWorkspace().id;
for (auto it = clients.begin(); it != clients.end(); ) { for (auto it = clients.begin(); it != clients.end();) {
if (it->workspace.id != activeWorkspaceID) { if (it->workspace.id != activeWorkspaceID) {
it = clients.erase(it); it = clients.erase(it);
} else { } else {
@@ -50,47 +75,42 @@ std::list<Client> HyprlandService::getClientsOnActiveWorkspace() {
std::list<WindowRule> HyprlandService::getWindowRules() { std::list<WindowRule> HyprlandService::getWindowRules() {
std::list<WindowRule> rules; std::list<WindowRule> rules;
for (auto& line : FileService::readLines(getConfigFilePath())) { for (auto &line : FileService::readLines(getConfigFilePath())) {
rules.push_back(WindowRule::parse(line)); rules.push_back(WindowRule::parse(line));
} }
return rules; return rules;
}; };
void HyprlandService::setClientFloating(Client& c) { void HyprlandService::setClientFloating(Client c) {
ShellService::exec(HYPRCTL_BINARY " dispatch setfloating address:" + c.address); ShellService::exec(HYPRCTL_BINARY " dispatch setfloating address:" +
c.address);
}; };
void HyprlandService::setClientTiled(Client& c) { void HyprlandService::setClientTiled(Client c) {
ShellService::exec(HYPRCTL_BINARY " dispatch settiled address:" + c.address); ShellService::exec(HYPRCTL_BINARY " dispatch settiled address:" + c.address);
} }
void HyprlandService::toggleClientFloating(Client& c) { // on = true -> creates a window rule to ENABLE floating mode for currently
ShellService::exec(HYPRCTL_BINARY " dispatch togglefloating address:" + c.address); // active workspace on = false -> creates a window rule to DISABLE floating mode
}; // for currently active workspace
//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) { void HyprlandService::setFloatingRule(bool on) {
WindowRule rule {.tile = !on, .workspaceID = getCurrentWorkspace().id}; WindowRule rule{.tile = !on, .workspaceID = getCurrentWorkspace().id};
auto conflictingRule = findConflictingRule(rule); auto conflictingRule = findConflictingRule(rule);
if (conflictingRule.has_value()) { if (conflictingRule.has_value()) {
removeRule(conflictingRule.value()); removeRule(conflictingRule.value());
} }
FileService::appendToFile( FileService::appendToFile(getConfigFilePath(), rule.toString());
getConfigFilePath(),
rule.toString()
);
}; };
std::optional<WindowRule> HyprlandService::findConflictingRule(WindowRule subject) { std::optional<WindowRule>
HyprlandService::findConflictingRule(WindowRule subject) {
std::list<WindowRule> rules = getWindowRules(); std::list<WindowRule> rules = getWindowRules();
int id = getCurrentWorkspace().id; int id = getCurrentWorkspace().id;
for (auto& rule : rules) { for (auto &rule : rules) {
if (rule.tile == !subject.tile && rule.workspaceID == subject.workspaceID) if (rule.tile == !subject.tile && rule.workspaceID == subject.workspaceID) {
{
return rule; return rule;
} }
} }
@@ -103,9 +123,8 @@ void HyprlandService::removeRule(WindowRule rule) {
int index = 0; int index = 0;
int foundIndex = -1; int foundIndex = -1;
for (auto& it : rules) { for (auto &it : rules) {
if (it.toString() == rule.toString()) if (it.toString() == rule.toString()) {
{
foundIndex = index; foundIndex = index;
break; break;
} }
@@ -113,25 +132,63 @@ void HyprlandService::removeRule(WindowRule rule) {
} }
if (foundIndex != -1) { if (foundIndex != -1) {
FileService::deleteNthLine( FileService::deleteNthLine(getConfigFilePath(), foundIndex);
getConfigFilePath(),
foundIndex
);
} }
//else: rule not found, do nothing // else: rule not found, do nothing
} }
bool HyprlandService::isFloatingRulePresent() { bool HyprlandService::isFloatingRulePresent(int workspaceId) {
//Checks if there's a valid window rule in place that enables floating mode for the currently active workspace
std::list<WindowRule> rules = getWindowRules(); std::list<WindowRule> rules = getWindowRules();
int id = getCurrentWorkspace().id;
for (auto& rule : rules) { for (auto &rule : rules) {
if (rule.workspaceID == id && rule.tile == false) { if (rule.workspaceID == workspaceId && rule.tile == false) {
return true; return true;
} }
} }
return false; return false;
}
bool HyprlandService::isFloatingRulePresent(Workspace workspace) {
return isFloatingRulePresent(workspace.id);
}; };
WindowRule HyprlandService::getActiveWorkspaceRule() {
std::list<WindowRule> rules = getWindowRules();
int id = getCurrentWorkspace().id;
for (auto &rule : rules) {
if (rule.workspaceID == id) {
return rule;
}
}
// If no rule is found, return a default rule (tiled)
return WindowRule{.tile = true, .workspaceID = id};
};
void HyprlandService::moveToWorkspace(int workspaceId) {
if (isFloatingRulePresent(workspaceId)) {
setClientFloating(getActiveClient());
} else {
setClientTiled(getActiveClient());
}
ShellService::exec(HYPRCTL_BINARY " dispatch movetoworkspace " +
std::to_string(workspaceId));
}
void HyprlandService::toggleFloating() {
if (isFloatingRulePresent(getCurrentWorkspace())) {
for (auto &c : getClientsOnActiveWorkspace()) {
setClientTiled(c);
}
setFloatingRule(false);
} else {
for (auto &c : getClientsOnActiveWorkspace()) {
setClientFloating(c);
}
setFloatingRule(true);
}
}

View File

@@ -1,12 +1,12 @@
#include "../include/ShellService.h" #include "../include/ShellService.hpp"
#include <pwd.h> #include <pwd.h>
#include <unistd.h> #include <unistd.h>
std::string ShellService::exec(const std::string& command) { std::string ShellService::exec(const std::string &command) {
char buffer[128]; char buffer[128];
std::string result = ""; std::string result = "";
FILE* pipe = popen(command.c_str(), "r"); FILE *pipe = popen(command.c_str(), "r");
if (!pipe) { if (!pipe) {
return "popen failed"; return "popen failed";
} }
@@ -23,16 +23,17 @@ std::string ShellService::exec(const std::string& command) {
return result; return result;
}; };
std::string ShellService::getHomePath() std::string ShellService::getHomePath() {
{ char *home = getenv("HOME");
char* home = getenv("HOME");
if (home && *home) { if (home && *home) {
return std::string(home); return std::string(home) + std::string("/");
} }
// Fallback: try to get home directory from passwd if HOME is not set // Fallback: try to get home directory from passwd if HOME is not set
// This assumes that the 'htt' process is ran with the user as the process owner. Should it be run with systemd or some other wonky method, it will behave unexpectedly // This assumes that the 'htt' process is ran with the user as the process
struct passwd* pw = getpwuid(getuid()); // owner. Should it be run with systemd or some other wonky method, it will
// behave unexpectedly
struct passwd *pw = getpwuid(getuid());
if (pw && pw->pw_dir) { if (pw && pw->pw_dir) {
return std::string(pw->pw_dir); return std::string(pw->pw_dir);
} }

View File

@@ -1,5 +1,5 @@
#include <algorithm> #include <algorithm>
#include "../include/WindowRule.h" #include "../include/WindowRule.hpp"
std::string WindowRule::toString() { std::string WindowRule::toString() {
std::string mode = this->tile ? "tile on" : "float on"; std::string mode = this->tile ? "tile on" : "float on";

View File

@@ -1,4 +1,4 @@
#include "../include/Workspace.h" #include "../include/Workspace.hpp"
void from_json(const nlohmann::json &j, Workspace &w) { void from_json(const nlohmann::json &j, Workspace &w) {
j.at("id").get_to(w.id); j.at("id").get_to(w.id);

View File

@@ -1,23 +1,36 @@
#include <iostream> #include <iostream>
#include "../include/HyprlandService.h" #include <stdexcept>
#include "../include/HyprlandService.hpp"
int main(int argc, char** argv){ void help(char* execPath) {
if (argc < 2) { std::cerr << "Usage: " << execPath << " <config_file_path> (flags)\n\n";
std::cerr << "Usage: " << argv[0] << " <config_file_path>\n"; std::cerr << "Flags:\n\n";
std::cerr << "-q:\t\tQuery current windowing mode, don't change anything.\n\t\tReturns Hyprland window rule active on current workspace.\n\t\tIf no rule is active, returns a default tiled rule.\n\n";
std::cerr << "-m [integer]:\tMove currently active window to specified workspace.\n\t\tUpon moving, the window will adapt to the windowing mode of the new workspace.\n\t\tDoesn't work with -q.\n";
exit(1); exit(1);
} }
int main(int argc, char** argv) {
if (argc >= 2) {
HyprlandService::setConfigFilePath(argv[1]); HyprlandService::setConfigFilePath(argv[1]);
if (HyprlandService::isFloatingRulePresent()) { if (argc == 2) {
for (auto& c : HyprlandService::getClientsOnActiveWorkspace()) { HyprlandService::toggleFloating();
HyprlandService::setClientTiled(c); }
else if (argc == 3 && argv[2] == std::string("-q")) {
std::cout << HyprlandService::getActiveWorkspaceRule().toString() << std::endl;
} else if (argc == 4 && argv[2] == std::string("-m")) {
try {
HyprlandService::moveToWorkspace(std::stoi(argv[3]));
} catch (std::invalid_argument) {
help(argv[0]);
} }
HyprlandService::setFloatingRule(false);
} else { } else {
for (auto& c : HyprlandService::getClientsOnActiveWorkspace()) { help(argv[0]);
HyprlandService::setClientFloating(c);
} }
HyprlandService::setFloatingRule(true); } else {
help(argv[0]);
} }
exit(0);
} }