Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a91cd16bbe | |||
| 296dc33ae4 | |||
| 7a6203a77b | |||
| 095c242fcf | |||
| c2c695ff45 | |||
| 1f1b60c975 | |||
| 0d453ab86b | |||
| f7516518e0 | |||
| 9c821d3e7b | |||
| 468daecbe9 | |||
| 982336be7c | |||
| 44bcbda690 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
.cache/
|
.cache/
|
||||||
build/
|
build/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
result
|
||||||
|
|||||||
@@ -1,17 +1,69 @@
|
|||||||
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
|
||||||
src/HyprlandService.cpp
|
src/HyprlandService.cpp
|
||||||
src/Workspace.cpp
|
src/Workspace.cpp
|
||||||
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}
|
||||||
|
)
|
||||||
|
|||||||
81
README.md
81
README.md
@@ -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:
|
||||||
|

|
||||||
|
|
||||||
|
- `-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  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  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
BIN
assets/waybar_module.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
37
default.nix
Normal file
37
default.nix
Normal 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
27
flake.lock
generated
Normal 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
20
flake.nix
Normal 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -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
|
||||||
@@ -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
9
include/Macros.hpp
Normal 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
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -60,4 +60,4 @@ std::vector<std::string> FileService::readLines(std::string path) {
|
|||||||
lines.push_back(line);
|
lines.push_back(line);
|
||||||
}
|
}
|
||||||
return lines;
|
return lines;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,42 +1,43 @@
|
|||||||
#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";
|
||||||
}
|
}
|
||||||
|
|
||||||
while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
|
while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
|
||||||
result += buffer;
|
result += buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
int status = pclose(pipe);
|
int status = pclose(pipe);
|
||||||
if (status == -1) {
|
if (status == -1) {
|
||||||
return "Error closing pipe";
|
return "Error closing pipe";
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// As a last resort, exit
|
// As a last resort, exit
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
45
src/main.cpp
45
src/main.cpp
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
HyprlandService::setConfigFilePath(argv[1]);
|
int main(int argc, char** argv) {
|
||||||
|
if (argc >= 2) {
|
||||||
|
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")) {
|
||||||
HyprlandService::setFloatingRule(false);
|
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]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
help(argv[0]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (auto& c : HyprlandService::getClientsOnActiveWorkspace()) {
|
help(argv[0]);
|
||||||
HyprlandService::setClientFloating(c);
|
|
||||||
}
|
|
||||||
HyprlandService::setFloatingRule(true);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user