Compare commits
No commits in common. "main" and "v0.54.0" have entirely different histories.
176 changed files with 3163 additions and 6240 deletions
|
|
@ -125,7 +125,6 @@ find_package(Threads REQUIRED)
|
||||||
|
|
||||||
set(GLES_VERSION "GLES3")
|
set(GLES_VERSION "GLES3")
|
||||||
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
|
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
|
||||||
find_package(glslang CONFIG REQUIRED)
|
|
||||||
|
|
||||||
set(AQUAMARINE_MINIMUM_VERSION 0.9.3)
|
set(AQUAMARINE_MINIMUM_VERSION 0.9.3)
|
||||||
set(HYPRLANG_MINIMUM_VERSION 0.6.7)
|
set(HYPRLANG_MINIMUM_VERSION 0.6.7)
|
||||||
|
|
@ -267,8 +266,7 @@ pkg_check_modules(
|
||||||
gbm
|
gbm
|
||||||
gio-2.0
|
gio-2.0
|
||||||
re2
|
re2
|
||||||
muparser
|
muparser)
|
||||||
lcms2)
|
|
||||||
|
|
||||||
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
|
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
|
||||||
|
|
||||||
|
|
@ -480,9 +478,9 @@ function(protocolWayland)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
if(TARGET OpenGL::GL)
|
if(TARGET OpenGL::GL)
|
||||||
target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads)
|
target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL Threads::Threads)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GLES3 glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads)
|
target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GLES3 Threads::Threads)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4)
|
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4)
|
||||||
|
|
|
||||||
1
Makefile
1
Makefile
|
|
@ -18,7 +18,6 @@ nopch:
|
||||||
clear:
|
clear:
|
||||||
rm -rf build
|
rm -rf build
|
||||||
rm -f ./protocols/*.h ./protocols/*.c ./protocols/*.cpp ./protocols/*.hpp
|
rm -f ./protocols/*.h ./protocols/*.c ./protocols/*.cpp ./protocols/*.hpp
|
||||||
rm -f ./hyprctl/hw-protocols/*.cpp ./hyprctl/hw-protocols/*.hpp
|
|
||||||
|
|
||||||
all:
|
all:
|
||||||
$(MAKE) clear
|
$(MAKE) clear
|
||||||
|
|
|
||||||
18
flake.lock
generated
18
flake.lock
generated
|
|
@ -16,11 +16,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772292445,
|
"lastModified": 1771610171,
|
||||||
"narHash": "sha256-4F1Q7U313TKUDDovCC96m/Za4wZcJ3yqtu4eSrj8lk8=",
|
"narHash": "sha256-+DeInuhbm6a6PpHDNUS7pozDouq2+8xSDefoNaZLW0E=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "aquamarine",
|
"repo": "aquamarine",
|
||||||
"rev": "1dbbba659c1cef0b0202ce92cadfe13bae550e8f",
|
"rev": "7f9eb087703ec4acc6b288d02fa9ea3db803cd3d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -325,11 +325,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772198003,
|
"lastModified": 1771848320,
|
||||||
"narHash": "sha256-I45esRSssFtJ8p/gLHUZ1OUaaTaVLluNkABkk6arQwE=",
|
"narHash": "sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "dd9b079222d43e1943b6ebd802f04fd959dc8e61",
|
"rev": "2fc6539b481e1d2569f25f8799236694180c0993",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -348,11 +348,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772024342,
|
"lastModified": 1771858127,
|
||||||
"narHash": "sha256-+eXlIc4/7dE6EcPs9a2DaSY3fTA9AE526hGqkNID3Wg=",
|
"narHash": "sha256-Gtre9YoYl3n25tJH2AoSdjuwcqij5CPxL3U3xysYD08=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "git-hooks.nix",
|
"repo": "git-hooks.nix",
|
||||||
"rev": "6e34e97ed9788b17796ee43ccdbaf871a5c2b476",
|
"rev": "49bbbfc218bf3856dfa631cead3b052d78248b83",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
||||||
206
flake.nix
206
flake.nix
|
|
@ -88,122 +88,108 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs = inputs @ {
|
||||||
inputs@{
|
self,
|
||||||
self,
|
nixpkgs,
|
||||||
nixpkgs,
|
systems,
|
||||||
systems,
|
...
|
||||||
...
|
}: let
|
||||||
}:
|
inherit (nixpkgs) lib;
|
||||||
let
|
eachSystem = lib.genAttrs (import systems);
|
||||||
inherit (nixpkgs) lib;
|
pkgsFor = eachSystem (system:
|
||||||
eachSystem = lib.genAttrs (import systems);
|
import nixpkgs {
|
||||||
pkgsFor = eachSystem (
|
localSystem = system;
|
||||||
system:
|
overlays = with self.overlays; [
|
||||||
import nixpkgs {
|
hyprland-packages
|
||||||
localSystem = system;
|
hyprland-extras
|
||||||
overlays = with self.overlays; [
|
];
|
||||||
hyprland-packages
|
});
|
||||||
hyprland-extras
|
pkgsCrossFor = eachSystem (system: crossSystem:
|
||||||
];
|
import nixpkgs {
|
||||||
}
|
localSystem = system;
|
||||||
);
|
inherit crossSystem;
|
||||||
pkgsCrossFor = eachSystem (
|
overlays = with self.overlays; [
|
||||||
system: crossSystem:
|
hyprland-packages
|
||||||
import nixpkgs {
|
hyprland-extras
|
||||||
localSystem = system;
|
];
|
||||||
inherit crossSystem;
|
});
|
||||||
overlays = with self.overlays; [
|
pkgsDebugFor = eachSystem (system:
|
||||||
hyprland-packages
|
import nixpkgs {
|
||||||
hyprland-extras
|
localSystem = system;
|
||||||
];
|
overlays = with self.overlays; [
|
||||||
}
|
hyprland-debug
|
||||||
);
|
];
|
||||||
pkgsDebugFor = eachSystem (
|
});
|
||||||
system:
|
pkgsDebugCrossFor = eachSystem (system: crossSystem:
|
||||||
import nixpkgs {
|
import nixpkgs {
|
||||||
localSystem = system;
|
localSystem = system;
|
||||||
overlays = with self.overlays; [
|
inherit crossSystem;
|
||||||
hyprland-debug
|
overlays = with self.overlays; [
|
||||||
];
|
hyprland-debug
|
||||||
}
|
];
|
||||||
);
|
});
|
||||||
pkgsDebugCrossFor = eachSystem (
|
in {
|
||||||
system: crossSystem:
|
overlays = import ./nix/overlays.nix {inherit self lib inputs;};
|
||||||
import nixpkgs {
|
|
||||||
localSystem = system;
|
|
||||||
inherit crossSystem;
|
|
||||||
overlays = with self.overlays; [
|
|
||||||
hyprland-debug
|
|
||||||
];
|
|
||||||
}
|
|
||||||
);
|
|
||||||
in
|
|
||||||
{
|
|
||||||
overlays = import ./nix/overlays.nix { inherit self lib inputs; };
|
|
||||||
|
|
||||||
checks = eachSystem (
|
checks = eachSystem (system:
|
||||||
system:
|
(lib.filterAttrs
|
||||||
(lib.filterAttrs (
|
(n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n))
|
||||||
n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n)
|
self.packages.${system})
|
||||||
) self.packages.${system})
|
// {
|
||||||
// {
|
inherit (self.packages.${system}) xdg-desktop-portal-hyprland;
|
||||||
inherit (self.packages.${system}) xdg-desktop-portal-hyprland;
|
pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
|
||||||
pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
|
src = ./.;
|
||||||
src = ./.;
|
hooks = {
|
||||||
hooks = {
|
hyprland-treewide-formatter = {
|
||||||
hyprland-treewide-formatter = {
|
enable = true;
|
||||||
enable = true;
|
entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter";
|
||||||
entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter";
|
pass_filenames = false;
|
||||||
pass_filenames = false;
|
excludes = ["subprojects"];
|
||||||
excludes = [ "subprojects" ];
|
always_run = true;
|
||||||
always_run = true;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
// (import ./nix/tests inputs pkgsFor.${system})
|
}
|
||||||
);
|
// (import ./nix/tests inputs pkgsFor.${system}));
|
||||||
|
|
||||||
packages = eachSystem (system: {
|
packages = eachSystem (system: {
|
||||||
default = self.packages.${system}.hyprland;
|
default = self.packages.${system}.hyprland;
|
||||||
inherit (pkgsFor.${system})
|
inherit
|
||||||
# hyprland-packages
|
(pkgsFor.${system})
|
||||||
hyprland
|
# hyprland-packages
|
||||||
hyprland-unwrapped
|
hyprland
|
||||||
hyprland-with-tests
|
hyprland-unwrapped
|
||||||
# hyprland-extras
|
hyprland-with-tests
|
||||||
xdg-desktop-portal-hyprland
|
# hyprland-extras
|
||||||
;
|
xdg-desktop-portal-hyprland
|
||||||
inherit (pkgsDebugFor.${system}) hyprland-debug;
|
;
|
||||||
hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland;
|
inherit (pkgsDebugFor.${system}) hyprland-debug;
|
||||||
hyprland-debug-cross = (pkgsDebugCrossFor.${system} "aarch64-linux").hyprland-debug;
|
hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland;
|
||||||
});
|
hyprland-debug-cross = (pkgsDebugCrossFor.${system} "aarch64-linux").hyprland-debug;
|
||||||
|
});
|
||||||
|
|
||||||
devShells = eachSystem (system: {
|
devShells = eachSystem (system: {
|
||||||
default =
|
default =
|
||||||
pkgsFor.${system}.mkShell.override
|
pkgsFor.${system}.mkShell.override {
|
||||||
{
|
inherit (self.packages.${system}.default) stdenv;
|
||||||
inherit (self.packages.${system}.default) stdenv;
|
} {
|
||||||
}
|
name = "hyprland-shell";
|
||||||
{
|
hardeningDisable = ["fortify"];
|
||||||
name = "hyprland-shell";
|
inputsFrom = [pkgsFor.${system}.hyprland];
|
||||||
hardeningDisable = [ "fortify" ];
|
packages = [pkgsFor.${system}.clang-tools];
|
||||||
inputsFrom = [ pkgsFor.${system}.hyprland ];
|
inherit (self.checks.${system}.pre-commit-check) shellHook;
|
||||||
packages = [ pkgsFor.${system}.clang-tools ];
|
};
|
||||||
inherit (self.checks.${system}.pre-commit-check) shellHook;
|
});
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix { });
|
formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix {});
|
||||||
|
|
||||||
nixosModules.default = import ./nix/module.nix inputs;
|
nixosModules.default = import ./nix/module.nix inputs;
|
||||||
homeManagerModules.default = import ./nix/hm-module.nix self;
|
homeManagerModules.default = import ./nix/hm-module.nix self;
|
||||||
|
|
||||||
# Hydra build jobs
|
# Hydra build jobs
|
||||||
# Recent versions of Hydra can aggregate jobsets from 'hydraJobs' instead of a release.nix
|
# Recent versions of Hydra can aggregate jobsets from 'hydraJobs' instead of a release.nix
|
||||||
# or similar. Remember to filter large or incompatible attributes here. More eval jobs can
|
# or similar. Remember to filter large or incompatible attributes here. More eval jobs can
|
||||||
# be added by merging, e.g., self.packages // self.devShells.
|
# be added by merging, e.g., self.packages // self.devShells.
|
||||||
hydraJobs = self.packages;
|
hydraJobs = self.packages;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -228,23 +228,23 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) {
|
||||||
constexpr size_t BUFFER_SIZE = 8192;
|
constexpr size_t BUFFER_SIZE = 8192;
|
||||||
char buffer[BUFFER_SIZE] = {0};
|
char buffer[BUFFER_SIZE] = {0};
|
||||||
|
|
||||||
// read all data until server closes the connection
|
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||||
// this handles partial writes on the server side under high load
|
|
||||||
while (true) {
|
|
||||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
|
||||||
|
|
||||||
|
if (sizeWritten < 0) {
|
||||||
|
if (errno == EWOULDBLOCK)
|
||||||
|
log("Hyprland IPC didn't respond in time\n");
|
||||||
|
log("Couldn't read (6)");
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply += std::string(buffer, sizeWritten);
|
||||||
|
|
||||||
|
while (sizeWritten == BUFFER_SIZE) {
|
||||||
|
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||||
if (sizeWritten < 0) {
|
if (sizeWritten < 0) {
|
||||||
if (errno == EWOULDBLOCK)
|
|
||||||
log("Hyprland IPC didn't respond in time\n");
|
|
||||||
log("Couldn't read (6)");
|
log("Couldn't read (6)");
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sizeWritten == 0) {
|
|
||||||
// server closed connection, we're done
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
reply += std::string(buffer, sizeWritten);
|
reply += std::string(buffer, sizeWritten);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ set(CMAKE_CXX_STANDARD 23)
|
||||||
|
|
||||||
pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.7.0)
|
pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.7.0)
|
||||||
|
|
||||||
find_package(glaze 6.0.1 QUIET)
|
find_package(glaze 7.0.0 QUIET)
|
||||||
if (NOT glaze_FOUND)
|
if (NOT glaze_FOUND)
|
||||||
set(GLAZE_VERSION v6.0.1)
|
set(GLAZE_VERSION v7.0.0)
|
||||||
message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent")
|
message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent")
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
|
|
|
||||||
|
|
@ -131,18 +131,9 @@ bool CPluginManager::createSafeDirectory(const std::string& path) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPluginManager::validArg(const std::string& s) {
|
|
||||||
return !s.contains("'") && !s.ends_with("\\") && !s.starts_with("\\");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& rev) {
|
bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& rev) {
|
||||||
const auto HLVER = getHyprlandVersion();
|
const auto HLVER = getHyprlandVersion();
|
||||||
|
|
||||||
if (!validArg(url) || !validArg(rev)) {
|
|
||||||
std::println(stderr, "\n{}", failureString("url or rev invalid"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasDeps()) {
|
if (!hasDeps()) {
|
||||||
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, cpio, pkg-config, git, g++, gcc"));
|
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, cpio, pkg-config, git, g++, gcc"));
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -207,7 +198,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||||
|
|
||||||
progress.printMessageAbove(infoString("Cloning {}", url));
|
progress.printMessageAbove(infoString("Cloning {}", url));
|
||||||
|
|
||||||
std::string ret = execAndGet(std::format("cd {} && git clone --recursive '{}' {}", getTempRoot(), url, USERNAME));
|
std::string ret = execAndGet(std::format("cd {} && git clone --recursive {} {}", getTempRoot(), url, USERNAME));
|
||||||
|
|
||||||
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
|
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
|
||||||
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. shell returned:\n{}", ret));
|
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. shell returned:\n{}", ret));
|
||||||
|
|
@ -512,11 +503,11 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||||
progress.printMessageAbove(verboseString("will shallow since: {}", SHALLOW_DATE));
|
progress.printMessageAbove(verboseString("will shallow since: {}", SHALLOW_DATE));
|
||||||
|
|
||||||
std::string ret =
|
std::string ret =
|
||||||
execAndGet(std::format("cd {} && git clone --recursive '{}' hyprland-{}{}", getTempRoot(), HL_URL, USERNAME, (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")));
|
execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}{}", getTempRoot(), HL_URL, USERNAME, (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")));
|
||||||
|
|
||||||
if (!std::filesystem::exists(WORKINGDIR)) {
|
if (!std::filesystem::exists(WORKINGDIR)) {
|
||||||
progress.printMessageAbove(failureString("Clone failed. Retrying without shallow."));
|
progress.printMessageAbove(failureString("Clone failed. Retrying without shallow."));
|
||||||
ret = execAndGet(std::format("cd {} && git clone --recursive '{}' hyprland-{}", getTempRoot(), HL_URL, USERNAME));
|
ret = execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}", getTempRoot(), HL_URL, USERNAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!std::filesystem::exists(WORKINGDIR + "/.git")) {
|
if (!std::filesystem::exists(WORKINGDIR + "/.git")) {
|
||||||
|
|
@ -657,7 +648,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||||
const auto HLVER = getHyprlandVersion(false);
|
const auto HLVER = getHyprlandVersion(false);
|
||||||
|
|
||||||
CProgressBar progress;
|
CProgressBar progress;
|
||||||
progress.m_iMaxSteps = (REPOS.size() * 2) + 2;
|
progress.m_iMaxSteps = REPOS.size() * 2 + 2;
|
||||||
progress.m_iSteps = 0;
|
progress.m_iSteps = 0;
|
||||||
progress.m_szCurrentMessage = "Updating repositories";
|
progress.m_szCurrentMessage = "Updating repositories";
|
||||||
progress.print();
|
progress.print();
|
||||||
|
|
@ -678,7 +669,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||||
|
|
||||||
progress.printMessageAbove(infoString("Cloning {}", repo.url));
|
progress.printMessageAbove(infoString("Cloning {}", repo.url));
|
||||||
|
|
||||||
std::string ret = execAndGet(std::format("cd {} && git clone --recursive '{}' {}", getTempRoot(), repo.url, USERNAME));
|
std::string ret = execAndGet(std::format("cd {} && git clone --recursive {} {}", getTempRoot(), repo.url, USERNAME));
|
||||||
|
|
||||||
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
|
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
|
||||||
std::println("{}", failureString("could not clone repo: shell returned: {}", ret));
|
std::println("{}", failureString("could not clone repo: shell returned: {}", ret));
|
||||||
|
|
@ -688,7 +679,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||||
if (!repo.rev.empty()) {
|
if (!repo.rev.empty()) {
|
||||||
progress.printMessageAbove(infoString("Plugin has revision set, resetting: {}", repo.rev));
|
progress.printMessageAbove(infoString("Plugin has revision set, resetting: {}", repo.rev));
|
||||||
|
|
||||||
std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules \'" + repo.rev + "\'");
|
std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + repo.rev);
|
||||||
if (ret.compare(0, 6, "fatal:") == 0) {
|
if (ret.compare(0, 6, "fatal:") == 0) {
|
||||||
std::println(stderr, "\n{}", failureString("could not check out revision {}: shell returned:\n{}", repo.rev, ret));
|
std::println(stderr, "\n{}", failureString("could not check out revision {}: shell returned:\n{}", repo.rev, ret));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,6 @@ class CPluginManager {
|
||||||
private:
|
private:
|
||||||
std::string headerError(const eHeadersErrors err);
|
std::string headerError(const eHeadersErrors err);
|
||||||
std::string headerErrorShort(const eHeadersErrors err);
|
std::string headerErrorShort(const eHeadersErrors err);
|
||||||
bool validArg(const std::string& s);
|
|
||||||
|
|
||||||
std::expected<std::string, std::string> nixDevelopIfNeeded(const std::string& cmd, const SHyprlandVersion& ver);
|
std::expected<std::string, std::string> nixDevelopIfNeeded(const std::string& cmd, const SHyprlandVersion& ver);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,7 @@
|
||||||
#include <src/managers/PointerManager.hpp>
|
#include <src/managers/PointerManager.hpp>
|
||||||
#include <src/managers/input/trackpad/TrackpadGestures.hpp>
|
#include <src/managers/input/trackpad/TrackpadGestures.hpp>
|
||||||
#include <src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
|
#include <src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
|
||||||
#include <src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp>
|
|
||||||
#include <src/desktop/rule/windowRule/WindowRuleApplicator.hpp>
|
#include <src/desktop/rule/windowRule/WindowRuleApplicator.hpp>
|
||||||
#include <src/desktop/view/LayerSurface.hpp>
|
|
||||||
#include <src/Compositor.hpp>
|
#include <src/Compositor.hpp>
|
||||||
#include <src/desktop/state/FocusState.hpp>
|
#include <src/desktop/state/FocusState.hpp>
|
||||||
#include <src/layout/LayoutManager.hpp>
|
#include <src/layout/LayoutManager.hpp>
|
||||||
|
|
@ -272,67 +270,32 @@ static SDispatchResult keybind(std::string in) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static Desktop::Rule::CWindowRuleEffectContainer::storageType windowRuleIDX = 0;
|
static Desktop::Rule::CWindowRuleEffectContainer::storageType ruleIDX = 0;
|
||||||
|
|
||||||
//
|
//
|
||||||
static SDispatchResult addWindowRule(std::string in) {
|
static SDispatchResult addRule(std::string in) {
|
||||||
windowRuleIDX = Desktop::Rule::windowEffects()->registerEffect("plugin_rule");
|
ruleIDX = Desktop::Rule::windowEffects()->registerEffect("plugin_rule");
|
||||||
|
|
||||||
if (Desktop::Rule::windowEffects()->registerEffect("plugin_rule") != windowRuleIDX)
|
if (Desktop::Rule::windowEffects()->registerEffect("plugin_rule") != ruleIDX)
|
||||||
return {.success = false, .error = "re-registering returned a different id?"};
|
return {.success = false, .error = "re-registering returned a different id?"};
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static SDispatchResult checkWindowRule(std::string in) {
|
static SDispatchResult checkRule(std::string in) {
|
||||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||||
|
|
||||||
if (!PLASTWINDOW)
|
if (!PLASTWINDOW)
|
||||||
return {.success = false, .error = "No window"};
|
return {.success = false, .error = "No window"};
|
||||||
|
|
||||||
if (!PLASTWINDOW->m_ruleApplicator->m_otherProps.props.contains(windowRuleIDX))
|
if (!PLASTWINDOW->m_ruleApplicator->m_otherProps.props.contains(ruleIDX))
|
||||||
return {.success = false, .error = "No rule"};
|
return {.success = false, .error = "No rule"};
|
||||||
|
|
||||||
if (PLASTWINDOW->m_ruleApplicator->m_otherProps.props[windowRuleIDX]->effect != "effect")
|
if (PLASTWINDOW->m_ruleApplicator->m_otherProps.props[ruleIDX]->effect != "effect")
|
||||||
return {.success = false, .error = "Effect isn't \"effect\""};
|
return {.success = false, .error = "Effect isn't \"effect\""};
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static Desktop::Rule::CLayerRuleEffectContainer::storageType layerRuleIDX = 0;
|
|
||||||
|
|
||||||
static SDispatchResult addLayerRule(std::string in) {
|
|
||||||
layerRuleIDX = Desktop::Rule::layerEffects()->registerEffect("plugin_rule");
|
|
||||||
|
|
||||||
if (Desktop::Rule::layerEffects()->registerEffect("plugin_rule") != layerRuleIDX)
|
|
||||||
return {.success = false, .error = "re-registering returned a different id?"};
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDispatchResult checkLayerRule(std::string in) {
|
|
||||||
if (g_pCompositor->m_layers.size() != 3)
|
|
||||||
return {.success = false, .error = "Layers under test not here"};
|
|
||||||
|
|
||||||
for (const auto& layer : g_pCompositor->m_layers) {
|
|
||||||
if (layer->m_namespace == "rule-layer") {
|
|
||||||
|
|
||||||
if (!layer->m_ruleApplicator->m_otherProps.props.contains(layerRuleIDX))
|
|
||||||
return {.success = false, .error = "No rule"};
|
|
||||||
|
|
||||||
if (layer->m_ruleApplicator->m_otherProps.props[layerRuleIDX]->effect != "effect")
|
|
||||||
return {.success = false, .error = "Effect isn't \"effect\""};
|
|
||||||
|
|
||||||
} else if (layer->m_namespace == "norule-layer") {
|
|
||||||
|
|
||||||
if (layer->m_ruleApplicator->m_otherProps.props.contains(layerRuleIDX))
|
|
||||||
return {.success = false, .error = "Rule even though it shouldn't"};
|
|
||||||
|
|
||||||
} else
|
|
||||||
return {.success = false, .error = "Unrecognized layer"};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDispatchResult floatingFocusOnFullscreen(std::string in) {
|
static SDispatchResult floatingFocusOnFullscreen(std::string in) {
|
||||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||||
|
|
||||||
|
|
@ -362,10 +325,8 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll);
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll);
|
||||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:click", ::click);
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:click", ::click);
|
||||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind);
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind);
|
||||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_window_rule", ::addWindowRule);
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_rule", ::addRule);
|
||||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_window_rule", ::checkWindowRule);
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_rule", ::checkRule);
|
||||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_layer_rule", ::addLayerRule);
|
|
||||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_layer_rule", ::checkLayerRule);
|
|
||||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:floating_focus_on_fullscreen", ::floatingFocusOnFullscreen);
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:floating_focus_on_fullscreen", ::floatingFocusOnFullscreen);
|
||||||
|
|
||||||
// init mouse
|
// init mouse
|
||||||
|
|
|
||||||
|
|
@ -39,16 +39,6 @@ namespace Colors {
|
||||||
TESTS_PASSED++; \
|
TESTS_PASSED++; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define EXPECT_NOT(expr, val) \
|
|
||||||
if (const auto RESULT = expr; RESULT == (val)) { \
|
|
||||||
NLog::log("{}Failed: {}{}, expected not {}, got {}. Source: {}@{}.", Colors::RED, Colors::RESET, #expr, val, RESULT, __FILE__, __LINE__); \
|
|
||||||
ret = 1; \
|
|
||||||
TESTS_FAILED++; \
|
|
||||||
} else { \
|
|
||||||
NLog::log("{}Passed: {}{}. Got {}", Colors::GREEN, Colors::RESET, #expr, val); \
|
|
||||||
TESTS_PASSED++; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define EXPECT_VECTOR2D(expr, val) \
|
#define EXPECT_VECTOR2D(expr, val) \
|
||||||
do { \
|
do { \
|
||||||
const auto& RESULT = expr; \
|
const auto& RESULT = expr; \
|
||||||
|
|
|
||||||
|
|
@ -118,33 +118,6 @@ static bool test() {
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
EXPECT(Tests::windowCount(), 0);
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
// test that child windows (shouldBeFloated) are not auto-grouped
|
|
||||||
NLog::log("{}Test child windows are not auto-grouped", Colors::GREEN);
|
|
||||||
auto kitty = Tests::spawnKitty();
|
|
||||||
if (!kitty) {
|
|
||||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create group and enable auto-grouping
|
|
||||||
OK(getFromSocket("/dispatch togglegroup"));
|
|
||||||
OK(getFromSocket("/keyword group:auto_group true"));
|
|
||||||
|
|
||||||
SClient client2;
|
|
||||||
if (!startClient(client2))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
EXPECT(Tests::windowCount(), 2);
|
|
||||||
createChild(client2);
|
|
||||||
EXPECT(Tests::windowCount(), 3);
|
|
||||||
|
|
||||||
// child has set_parent so shouldBeFloated returns true, it should not be auto-grouped
|
|
||||||
EXPECT_COUNT_STRING(getFromSocket("/clients"), "grouped: 0", 1);
|
|
||||||
|
|
||||||
stopClient(client2);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
EXPECT(Tests::windowCount(), 0);
|
|
||||||
|
|
||||||
return !ret;
|
return !ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,16 +64,16 @@ static void test13349() {
|
||||||
|
|
||||||
{
|
{
|
||||||
auto str = getFromSocket("/activewindow");
|
auto str = getFromSocket("/activewindow");
|
||||||
EXPECT_CONTAINS(str, "at: 497,22");
|
EXPECT_CONTAINS(str, "at: 22,547");
|
||||||
EXPECT_CONTAINS(str, "size: 456,1036");
|
EXPECT_CONTAINS(str, "size: 931,511");
|
||||||
}
|
}
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movewindow r"));
|
OK(getFromSocket("/dispatch movewindow r"));
|
||||||
|
|
||||||
{
|
{
|
||||||
auto str = getFromSocket("/activewindow");
|
auto str = getFromSocket("/activewindow");
|
||||||
EXPECT_CONTAINS(str, "at: 967,22");
|
EXPECT_CONTAINS(str, "at: 967,547");
|
||||||
EXPECT_CONTAINS(str, "size: 456,1036");
|
EXPECT_CONTAINS(str, "size: 931,511");
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
|
|
@ -81,152 +81,6 @@ static void test13349() {
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testSplit() {
|
|
||||||
// Test various split methods
|
|
||||||
|
|
||||||
Tests::spawnKitty("a");
|
|
||||||
|
|
||||||
// these must not crash
|
|
||||||
EXPECT_NOT(getFromSocket("/dispatch layoutmsg swapsplit"), "ok");
|
|
||||||
EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio 1 exact"), "ok");
|
|
||||||
|
|
||||||
Tests::spawnKitty("b");
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:a"));
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg splitratio -0.2"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 22,22");
|
|
||||||
EXPECT_CONTAINS(str, "size: 743,1036");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg splitratio 1.6 exact"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 22,22");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1495,1036");
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio fhne exact"), "ok");
|
|
||||||
EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio exact"), "ok");
|
|
||||||
EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio -....9"), "ok");
|
|
||||||
EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio ..9"), "ok");
|
|
||||||
EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio"), "ok");
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg togglesplit"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 22,22");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1876,823");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg swapsplit"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 22,859");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1876,199");
|
|
||||||
}
|
|
||||||
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void testRotatesplit() {
|
|
||||||
OK(getFromSocket("r/keyword general:gaps_in 0"));
|
|
||||||
OK(getFromSocket("r/keyword general:gaps_out 0"));
|
|
||||||
OK(getFromSocket("r/keyword general:border_size 0"));
|
|
||||||
|
|
||||||
for (auto const& win : {"a", "b"}) {
|
|
||||||
if (!Tests::spawnKitty(win)) {
|
|
||||||
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
|
||||||
++TESTS_FAILED;
|
|
||||||
ret = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_CONTAINS(str, "at: 0,0");
|
|
||||||
EXPECT_CONTAINS(str, "size: 960,1080");
|
|
||||||
}
|
|
||||||
|
|
||||||
// test 4 repeated rotations by 90 degrees
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg rotatesplit"));
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_CONTAINS(str, "at: 0,0");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1920,540");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg rotatesplit"));
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_CONTAINS(str, "at: 960,0");
|
|
||||||
EXPECT_CONTAINS(str, "size: 960,1080");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg rotatesplit"));
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_CONTAINS(str, "at: 0,540");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1920,540");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg rotatesplit"));
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_CONTAINS(str, "at: 0,0");
|
|
||||||
EXPECT_CONTAINS(str, "size: 960,1080");
|
|
||||||
}
|
|
||||||
|
|
||||||
// test different angles
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg rotatesplit 180"));
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_CONTAINS(str, "at: 960,0");
|
|
||||||
EXPECT_CONTAINS(str, "size: 960,1080");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg rotatesplit 270"));
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_CONTAINS(str, "at: 0,540");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1920,540");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg rotatesplit 360"));
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_CONTAINS(str, "at: 0,0");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1920,540");
|
|
||||||
}
|
|
||||||
|
|
||||||
// test negative angles
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg rotatesplit -90"));
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_CONTAINS(str, "at: 0,0");
|
|
||||||
EXPECT_CONTAINS(str, "size: 960,1080");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg rotatesplit -180"));
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_CONTAINS(str, "at: 960,0");
|
|
||||||
EXPECT_CONTAINS(str, "size: 960,1080");
|
|
||||||
}
|
|
||||||
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
|
|
||||||
OK(getFromSocket("/reload"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool test() {
|
static bool test() {
|
||||||
NLog::log("{}Testing Dwindle layout", Colors::GREEN);
|
NLog::log("{}Testing Dwindle layout", Colors::GREEN);
|
||||||
|
|
||||||
|
|
@ -237,12 +91,6 @@ static bool test() {
|
||||||
NLog::log("{}Testing #13349", Colors::GREEN);
|
NLog::log("{}Testing #13349", Colors::GREEN);
|
||||||
test13349();
|
test13349();
|
||||||
|
|
||||||
NLog::log("{}Testing splits", Colors::GREEN);
|
|
||||||
testSplit();
|
|
||||||
|
|
||||||
NLog::log("{}Testing rotatesplit", Colors::GREEN);
|
|
||||||
testRotatesplit();
|
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
NLog::log("Cleaning up", Colors::YELLOW);
|
NLog::log("Cleaning up", Colors::YELLOW);
|
||||||
getFromSocket("/dispatch workspace 1");
|
getFromSocket("/dispatch workspace 1");
|
||||||
|
|
|
||||||
|
|
@ -127,34 +127,6 @@ static bool test() {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// test movegroupwindow: focus should follow the moved window
|
|
||||||
NLog::log("{}Test movegroupwindow focus follows window", Colors::YELLOW);
|
|
||||||
try {
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
auto activeBeforeMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16);
|
|
||||||
OK(getFromSocket("/dispatch movegroupwindow f"));
|
|
||||||
str = getFromSocket("/activewindow");
|
|
||||||
auto activeAfterMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16);
|
|
||||||
EXPECT(activeAfterMove, activeBeforeMove);
|
|
||||||
} catch (...) {
|
|
||||||
NLog::log("{}Fail at getting prop", Colors::RED);
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// and backwards
|
|
||||||
NLog::log("{}Test movegroupwindow backwards", Colors::YELLOW);
|
|
||||||
try {
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
auto activeBeforeMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16);
|
|
||||||
OK(getFromSocket("/dispatch movegroupwindow b"));
|
|
||||||
str = getFromSocket("/activewindow");
|
|
||||||
auto activeAfterMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16);
|
|
||||||
EXPECT(activeAfterMove, activeBeforeMove);
|
|
||||||
} catch (...) {
|
|
||||||
NLog::log("{}Fail at getting prop", Colors::RED);
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
NLog::log("{}Disable autogrouping", Colors::YELLOW);
|
NLog::log("{}Disable autogrouping", Colors::YELLOW);
|
||||||
OK(getFromSocket("/keyword group:auto_group false"));
|
OK(getFromSocket("/keyword group:auto_group false"));
|
||||||
|
|
||||||
|
|
@ -201,99 +173,6 @@ static bool test() {
|
||||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||||
EXPECT(Tests::windowCount(), 0);
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
// test movewindoworgroup: direction should be respected when extracting from group
|
|
||||||
NLog::log("{}Test movewindoworgroup respects direction out of group", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("/keyword group:groupbar:enabled 0"));
|
|
||||||
{
|
|
||||||
auto kittyE = Tests::spawnKitty();
|
|
||||||
if (!kittyE) {
|
|
||||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// group kitty, and new windows should be auto-grouped
|
|
||||||
OK(getFromSocket("/dispatch togglegroup"));
|
|
||||||
|
|
||||||
auto kittyF = Tests::spawnKitty();
|
|
||||||
if (!kittyF) {
|
|
||||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
EXPECT(Tests::windowCount(), 2);
|
|
||||||
|
|
||||||
// both windows should be grouped at the same position
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_COUNT_STRING(str, "at: 22,22", 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// move active window out of group to the right
|
|
||||||
NLog::log("{}Test movewindoworgroup r", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("/dispatch movewindoworgroup r"));
|
|
||||||
|
|
||||||
// the group should stay at x=22, the extracted window should be to the right
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_COUNT_STRING(str, "at: 22,22", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// move it back into the group
|
|
||||||
OK(getFromSocket("/dispatch moveintogroup l"));
|
|
||||||
|
|
||||||
// move active window out of group downward
|
|
||||||
NLog::log("{}Test movewindoworgroup d", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("/dispatch movewindoworgroup d"));
|
|
||||||
|
|
||||||
// the group should stay at y=22, the extracted window should be below
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/clients");
|
|
||||||
EXPECT_COUNT_STRING(str, "at: 22,22", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Tests::killAllWindows();
|
|
||||||
EXPECT(Tests::windowCount(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// test that we deny a floated window getting auto-grouped into a tiled group.
|
|
||||||
NLog::log("{}Test that we deny a floated window getting auto-grouped into a tiled group.", Colors::GREEN);
|
|
||||||
|
|
||||||
OK(getFromSocket("/keyword windowrule[kitty-tiled]:match:class kitty_tiled"));
|
|
||||||
OK(getFromSocket("/keyword windowrule[kitty-tiled]:tile yes"));
|
|
||||||
auto kittyProcE = Tests::spawnKitty("kitty_tiled");
|
|
||||||
if (!kittyProcE) {
|
|
||||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
OK(getFromSocket("/dispatch togglegroup"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/keyword windowrule[kitty-floated]:match:class kitty_floated"));
|
|
||||||
OK(getFromSocket("/keyword windowrule[kitty-floated]:float yes"));
|
|
||||||
auto kittyProcF = Tests::spawnKitty("kitty_floated");
|
|
||||||
if (!kittyProcF) {
|
|
||||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT(Tests::windowCount(), 2);
|
|
||||||
|
|
||||||
{
|
|
||||||
auto clients = getFromSocket("/clients");
|
|
||||||
auto classPos = clients.find("class: kitty_floated");
|
|
||||||
if (classPos == std::string::npos) {
|
|
||||||
NLog::log("{}Could not find kitty_floated in clients output", Colors::RED);
|
|
||||||
ret = 1;
|
|
||||||
} else {
|
|
||||||
auto entryStart = clients.rfind("Window ", classPos);
|
|
||||||
auto entryEnd = clients.find("\n\n", classPos);
|
|
||||||
auto windowEntry = clients.substr(entryStart, entryEnd - entryStart);
|
|
||||||
EXPECT_CONTAINS(windowEntry, "floating: 1");
|
|
||||||
EXPECT_CONTAINS(windowEntry, "grouped: 0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Tests::killAllWindows();
|
|
||||||
EXPECT(Tests::windowCount(), 0);
|
|
||||||
|
|
||||||
return !ret;
|
return !ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
#include "../../Log.hpp"
|
|
||||||
#include "../shared.hpp"
|
|
||||||
#include "tests.hpp"
|
|
||||||
#include "../../shared.hpp"
|
|
||||||
#include "../../hyprctlCompat.hpp"
|
|
||||||
#include <hyprutils/os/Process.hpp>
|
|
||||||
#include <hyprutils/memory/WeakPtr.hpp>
|
|
||||||
|
|
||||||
static int ret = 0;
|
|
||||||
|
|
||||||
using namespace Hyprutils::OS;
|
|
||||||
using namespace Hyprutils::Memory;
|
|
||||||
|
|
||||||
static bool spawnLayer(const std::string& namespace_) {
|
|
||||||
NLog::log("{}Spawning kitty layer {}", Colors::YELLOW, namespace_);
|
|
||||||
if (!Tests::spawnLayerKitty(namespace_)) {
|
|
||||||
NLog::log("{}Error: {} layer did not spawn", Colors::RED, namespace_);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool test() {
|
|
||||||
NLog::log("{}Testing plugin layerrules", Colors::GREEN);
|
|
||||||
|
|
||||||
if (!spawnLayer("rule-layer"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch plugin:test:add_layer_rule"));
|
|
||||||
OK(getFromSocket("/reload"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/keyword layerrule match:namespace rule-layer, plugin_rule effect"));
|
|
||||||
|
|
||||||
if (!spawnLayer("rule-layer"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!spawnLayer("norule-layer"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch plugin:test:check_layer_rule"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/reload"));
|
|
||||||
|
|
||||||
NLog::log("{}Killing all layers", Colors::YELLOW);
|
|
||||||
Tests::killAllLayers();
|
|
||||||
|
|
||||||
NLog::log("{}Expecting 0 layers", Colors::YELLOW);
|
|
||||||
EXPECT(Tests::layerCount(), 0);
|
|
||||||
|
|
||||||
return !ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_TEST_FN(test)
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
#include "../shared.hpp"
|
|
||||||
#include "../../shared.hpp"
|
|
||||||
#include "../../hyprctlCompat.hpp"
|
|
||||||
#include "tests.hpp"
|
|
||||||
|
|
||||||
static int ret = 0;
|
|
||||||
|
|
||||||
static void swar() {
|
|
||||||
OK(getFromSocket("/keyword layout:single_window_aspect_ratio 1 1"));
|
|
||||||
|
|
||||||
Tests::spawnKitty();
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 442,22");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1036,1036");
|
|
||||||
}
|
|
||||||
|
|
||||||
Tests::spawnKitty();
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch killwindow activewindow"));
|
|
||||||
|
|
||||||
Tests::waitUntilWindowsN(1);
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 442,22");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1036,1036");
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't use swar on maximized
|
|
||||||
OK(getFromSocket("/dispatch fullscreen 1"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 22,22");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1876,1036");
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't crash when focus after global geometry changes
|
|
||||||
static void testCrashOnGeomUpdate() {
|
|
||||||
Tests::spawnKitty();
|
|
||||||
Tests::spawnKitty();
|
|
||||||
Tests::spawnKitty();
|
|
||||||
|
|
||||||
// move the layout
|
|
||||||
OK(getFromSocket("/keyword monitor HEADLESS-2,1920x1080@60,1000x0,1"));
|
|
||||||
|
|
||||||
// shouldnt crash
|
|
||||||
OK(getFromSocket("/dispatch movefocus r"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/reload"));
|
|
||||||
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test if size + pos is preserved after fs cycle
|
|
||||||
static void testPosPreserve() {
|
|
||||||
Tests::spawnKitty();
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch setfloating class:kitty"));
|
|
||||||
OK(getFromSocket("/dispatch resizewindowpixel exact 1337 69, class:kitty"));
|
|
||||||
OK(getFromSocket("/dispatch movewindowpixel exact 420 420, class:kitty"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 420,420");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1337,69");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch fullscreen"));
|
|
||||||
OK(getFromSocket("/dispatch fullscreen"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1337,69");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movewindow r"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 581,420");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1337,69");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch fullscreen"));
|
|
||||||
OK(getFromSocket("/dispatch fullscreen"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 581,420");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1337,69");
|
|
||||||
}
|
|
||||||
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool test() {
|
|
||||||
NLog::log("{}Testing layout generic", Colors::GREEN);
|
|
||||||
|
|
||||||
// setup
|
|
||||||
OK(getFromSocket("/dispatch workspace 10"));
|
|
||||||
|
|
||||||
// test
|
|
||||||
NLog::log("{}Testing `single_window_aspect_ratio`", Colors::GREEN);
|
|
||||||
swar();
|
|
||||||
|
|
||||||
testCrashOnGeomUpdate();
|
|
||||||
testPosPreserve();
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
NLog::log("Cleaning up", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("/dispatch workspace 1"));
|
|
||||||
OK(getFromSocket("/reload"));
|
|
||||||
|
|
||||||
return !ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_TEST_FN(test);
|
|
||||||
|
|
@ -97,67 +97,6 @@ static void focusMasterPrevious() {
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testFsBehavior() {
|
|
||||||
// Master will re-send data to fullscreen / maximized windows, which can interfere with misc:on_focus_under_fullscreen
|
|
||||||
// check that it doesn't.
|
|
||||||
|
|
||||||
for (auto const& win : {"master", "slave1", "slave2"}) {
|
|
||||||
if (!Tests::spawnKitty(win)) {
|
|
||||||
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
|
||||||
++TESTS_FAILED;
|
|
||||||
ret = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:master"));
|
|
||||||
OK(getFromSocket("/dispatch fullscreen 1"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 22,22");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1876,1036");
|
|
||||||
EXPECT_CONTAINS(str, "class: master");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 1"));
|
|
||||||
|
|
||||||
Tests::spawnKitty("new_master");
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 22,22");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1876,1036");
|
|
||||||
EXPECT_CONTAINS(str, "class: new_master");
|
|
||||||
EXPECT_CONTAINS(str, "fullscreen: 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0"));
|
|
||||||
|
|
||||||
Tests::spawnKitty("ignored");
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "at: 22,22");
|
|
||||||
EXPECT_CONTAINS(str, "size: 1876,1036");
|
|
||||||
EXPECT_CONTAINS(str, "class: new_master");
|
|
||||||
EXPECT_CONTAINS(str, "fullscreen: 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 2"));
|
|
||||||
|
|
||||||
Tests::spawnKitty("vaxwashere");
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: vaxwashere");
|
|
||||||
EXPECT_CONTAINS(str, "fullscreen: 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool test() {
|
static bool test() {
|
||||||
NLog::log("{}Testing Master layout", Colors::GREEN);
|
NLog::log("{}Testing Master layout", Colors::GREEN);
|
||||||
|
|
||||||
|
|
@ -169,9 +108,6 @@ static bool test() {
|
||||||
NLog::log("{}Testing `focusmaster previous` layoutmsg", Colors::GREEN);
|
NLog::log("{}Testing `focusmaster previous` layoutmsg", Colors::GREEN);
|
||||||
focusMasterPrevious();
|
focusMasterPrevious();
|
||||||
|
|
||||||
NLog::log("{}Testing fs behavior", Colors::GREEN);
|
|
||||||
testFsBehavior();
|
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
NLog::log("Cleaning up", Colors::YELLOW);
|
NLog::log("Cleaning up", Colors::YELLOW);
|
||||||
OK(getFromSocket("/dispatch workspace 1"));
|
OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
|
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
#include "../shared.hpp"
|
|
||||||
#include "../../shared.hpp"
|
|
||||||
#include "../../hyprctlCompat.hpp"
|
|
||||||
#include "tests.hpp"
|
|
||||||
|
|
||||||
static int ret = 0;
|
|
||||||
|
|
||||||
static void testFocusCycling() {
|
|
||||||
for (auto const& win : {"a", "b", "c", "d"}) {
|
|
||||||
if (!Tests::spawnKitty(win)) {
|
|
||||||
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
|
||||||
++TESTS_FAILED;
|
|
||||||
ret = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:a"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movefocus r"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: b");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movefocus r"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: c");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movefocus r"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: d");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movewindow l"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: d");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movefocus u"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: c");
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void testFocusWrapping() {
|
|
||||||
for (auto const& win : {"a", "b", "c", "d"}) {
|
|
||||||
if (!Tests::spawnKitty(win)) {
|
|
||||||
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
|
||||||
++TESTS_FAILED;
|
|
||||||
ret = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set wrap_focus to true
|
|
||||||
OK(getFromSocket("/keyword scrolling:wrap_focus true"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:a"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg focus l"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: d");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg focus r"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: a");
|
|
||||||
}
|
|
||||||
|
|
||||||
// set wrap_focus to false
|
|
||||||
OK(getFromSocket("/keyword scrolling:wrap_focus false"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:a"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg focus l"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: a");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:d"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg focus r"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: d");
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void testSwapcolWrapping() {
|
|
||||||
for (auto const& win : {"a", "b", "c", "d"}) {
|
|
||||||
if (!Tests::spawnKitty(win)) {
|
|
||||||
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
|
||||||
++TESTS_FAILED;
|
|
||||||
ret = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set wrap_swapcol to true
|
|
||||||
OK(getFromSocket("/keyword scrolling:wrap_swapcol true"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:a"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg swapcol l"));
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg focus l"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: c");
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
|
|
||||||
for (auto const& win : {"a", "b", "c", "d"}) {
|
|
||||||
if (!Tests::spawnKitty(win)) {
|
|
||||||
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
|
||||||
++TESTS_FAILED;
|
|
||||||
ret = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:d"));
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg swapcol r"));
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg focus r"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: b");
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
|
|
||||||
for (auto const& win : {"a", "b", "c", "d"}) {
|
|
||||||
if (!Tests::spawnKitty(win)) {
|
|
||||||
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
|
||||||
++TESTS_FAILED;
|
|
||||||
ret = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set wrap_swapcol to false
|
|
||||||
OK(getFromSocket("/keyword scrolling:wrap_swapcol false"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:a"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg swapcol l"));
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg focus r"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: b");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:d"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg swapcol r"));
|
|
||||||
OK(getFromSocket("/dispatch layoutmsg focus l"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: c");
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool test() {
|
|
||||||
NLog::log("{}Testing Scroll layout", Colors::GREEN);
|
|
||||||
|
|
||||||
// setup
|
|
||||||
OK(getFromSocket("/dispatch workspace name:scroll"));
|
|
||||||
OK(getFromSocket("/keyword general:layout scrolling"));
|
|
||||||
|
|
||||||
// test
|
|
||||||
NLog::log("{}Testing focus cycling", Colors::GREEN);
|
|
||||||
testFocusCycling();
|
|
||||||
|
|
||||||
// test
|
|
||||||
NLog::log("{}Testing focus wrap", Colors::GREEN);
|
|
||||||
testFocusWrapping();
|
|
||||||
|
|
||||||
// test
|
|
||||||
NLog::log("{}Testing swapcol wrap", Colors::GREEN);
|
|
||||||
testSwapcolWrapping();
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
NLog::log("Cleaning up", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("/dispatch workspace 1"));
|
|
||||||
OK(getFromSocket("/reload"));
|
|
||||||
|
|
||||||
return !ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_TEST_FN(test);
|
|
||||||
|
|
@ -93,9 +93,9 @@ static void testSwapWindow() {
|
||||||
{
|
{
|
||||||
getFromSocket("/dispatch focuswindow class:kitty_A");
|
getFromSocket("/dispatch focuswindow class:kitty_A");
|
||||||
auto pos = getWindowAttribute(getFromSocket("/activewindow"), "at:");
|
auto pos = getWindowAttribute(getFromSocket("/activewindow"), "at:");
|
||||||
NLog::log("{}Testing kitty_A {}, swapwindow with direction 'r'", Colors::YELLOW, pos);
|
NLog::log("{}Testing kitty_A {}, swapwindow with direction 'l'", Colors::YELLOW, pos);
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch swapwindow r"));
|
OK(getFromSocket("/dispatch swapwindow l"));
|
||||||
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||||
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("{}", pos));
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("{}", pos));
|
||||||
|
|
@ -566,98 +566,6 @@ static bool testWindowRuleFocusOnActivate() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tests if a pinned window contains the valid workspace after change
|
|
||||||
static bool testPinnedWorkspacesValid() {
|
|
||||||
OK(getFromSocket("/reload"));
|
|
||||||
getFromSocket("/dispatch workspace 1337");
|
|
||||||
|
|
||||||
if (!spawnKitty("kitty")) {
|
|
||||||
NLog::log("{}Error: failed to spawn kitty", Colors::RED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch setfloating class:kitty"));
|
|
||||||
OK(getFromSocket("/dispatch pin class:kitty"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT(str.contains("workspace: 1337"), true);
|
|
||||||
EXPECT(str.contains("pinned: 1"), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
getFromSocket("/dispatch workspace 1338");
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT(str.contains("workspace: 1338"), true);
|
|
||||||
EXPECT(str.contains("pinned: 1"), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch settiled class:kitty"))
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT(str.contains("workspace: 1338"), true);
|
|
||||||
EXPECT(str.contains("pinned: 0"), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
NLog::log("{}Reloading config", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("/reload"));
|
|
||||||
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
|
|
||||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
|
||||||
EXPECT(Tests::windowCount(), 0);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool testWindowRuleWorkspaceEmpty() {
|
|
||||||
NLog::log("{}Testing windowrule workspace empty", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("/reload"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/keyword windowrule match:class kitty_A, workspace empty"));
|
|
||||||
OK(getFromSocket("/keyword windowrule match:class kitty_B, workspace emptyn"));
|
|
||||||
|
|
||||||
getFromSocket("/dispatch workspace 3");
|
|
||||||
|
|
||||||
if (!spawnKitty("kitty")) {
|
|
||||||
NLog::log("{}Error: failed to spawn kitty", Colors::RED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT(str.contains("workspace: 3"), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!spawnKitty("kitty_A")) {
|
|
||||||
NLog::log("{}Error: failed to spawn kitty", Colors::RED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT(str.contains("workspace: 1"), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
getFromSocket("/dispatch workspace 3");
|
|
||||||
if (!spawnKitty("kitty_B")) {
|
|
||||||
NLog::log("{}Error: failed to spawn kitty", Colors::RED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT(str.contains("workspace: 4"), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Tests::killAllWindows();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool test() {
|
static bool test() {
|
||||||
NLog::log("{}Testing windows", Colors::GREEN);
|
NLog::log("{}Testing windows", Colors::GREEN);
|
||||||
|
|
||||||
|
|
@ -1086,7 +994,7 @@ static bool test() {
|
||||||
OK(getFromSocket("/reload"));
|
OK(getFromSocket("/reload"));
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch plugin:test:add_window_rule"));
|
OK(getFromSocket("/dispatch plugin:test:add_rule"));
|
||||||
OK(getFromSocket("/reload"));
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
OK(getFromSocket("/keyword windowrule match:class plugin_kitty, plugin_rule effect"));
|
OK(getFromSocket("/keyword windowrule match:class plugin_kitty, plugin_rule effect"));
|
||||||
|
|
@ -1094,12 +1002,12 @@ static bool test() {
|
||||||
if (!spawnKitty("plugin_kitty"))
|
if (!spawnKitty("plugin_kitty"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch plugin:test:check_window_rule"));
|
OK(getFromSocket("/dispatch plugin:test:check_rule"));
|
||||||
|
|
||||||
OK(getFromSocket("/reload"));
|
OK(getFromSocket("/reload"));
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch plugin:test:add_window_rule"));
|
OK(getFromSocket("/dispatch plugin:test:add_rule"));
|
||||||
OK(getFromSocket("/reload"));
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
OK(getFromSocket("/keyword windowrule[test-plugin-rule]:match:class plugin_kitty"));
|
OK(getFromSocket("/keyword windowrule[test-plugin-rule]:match:class plugin_kitty"));
|
||||||
|
|
@ -1108,7 +1016,7 @@ static bool test() {
|
||||||
if (!spawnKitty("plugin_kitty"))
|
if (!spawnKitty("plugin_kitty"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch plugin:test:check_window_rule"));
|
OK(getFromSocket("/dispatch plugin:test:check_rule"));
|
||||||
|
|
||||||
OK(getFromSocket("/reload"));
|
OK(getFromSocket("/reload"));
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
|
|
@ -1120,8 +1028,6 @@ static bool test() {
|
||||||
testGroupFallbackFocus();
|
testGroupFallbackFocus();
|
||||||
testInitialFloatSize();
|
testInitialFloatSize();
|
||||||
testWindowRuleFocusOnActivate();
|
testWindowRuleFocusOnActivate();
|
||||||
testPinnedWorkspacesValid();
|
|
||||||
testWindowRuleWorkspaceEmpty();
|
|
||||||
|
|
||||||
NLog::log("{}Reloading config", Colors::YELLOW);
|
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||||
OK(getFromSocket("/reload"));
|
OK(getFromSocket("/reload"));
|
||||||
|
|
|
||||||
|
|
@ -255,144 +255,6 @@ static void testMultimonBAF() {
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testMultimonFocus() {
|
|
||||||
NLog::log("{}Testing multimon focus and move", Colors::YELLOW);
|
|
||||||
|
|
||||||
OK(getFromSocket("/keyword input:follow_mouse 0"));
|
|
||||||
OK(getFromSocket("/dispatch focusmonitor HEADLESS-3"));
|
|
||||||
OK(getFromSocket("/dispatch workspace 8"));
|
|
||||||
OK(getFromSocket("/dispatch focusmonitor HEADLESS-2"));
|
|
||||||
OK(getFromSocket("/dispatch workspace 7"));
|
|
||||||
|
|
||||||
for (auto const& win : {"a", "b"}) {
|
|
||||||
if (!Tests::spawnKitty(win)) {
|
|
||||||
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
|
||||||
++TESTS_FAILED;
|
|
||||||
ret = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:a"));
|
|
||||||
OK(getFromSocket("/dispatch movefocus r"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activeworkspace");
|
|
||||||
EXPECT_CONTAINS(str, "workspace ID 7 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: b");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movefocus r"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activeworkspace");
|
|
||||||
EXPECT_CONTAINS(str, "workspace ID 8 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
Tests::spawnKitty("c");
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: c");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activeworkspace");
|
|
||||||
EXPECT_CONTAINS(str, "workspace ID 8 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movefocus l"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: b");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activeworkspace");
|
|
||||||
EXPECT_CONTAINS(str, "workspace ID 7 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movewindow r"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: b");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activeworkspace");
|
|
||||||
EXPECT_CONTAINS(str, "workspace ID 8 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movefocus r"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: c");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activeworkspace");
|
|
||||||
EXPECT_CONTAINS(str, "workspace ID 8 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movefocus l"));
|
|
||||||
|
|
||||||
OK(getFromSocket("/keyword general:no_focus_fallback true"));
|
|
||||||
OK(getFromSocket("/keyword binds:window_direction_monitor_fallback false"));
|
|
||||||
|
|
||||||
EXPECT_NOT(getFromSocket("/dispatch movefocus l"), "ok");
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: b");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activeworkspace");
|
|
||||||
EXPECT_CONTAINS(str, "workspace ID 8 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch movewindow l"));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activewindow");
|
|
||||||
EXPECT_CONTAINS(str, "class: b");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto str = getFromSocket("/activeworkspace");
|
|
||||||
EXPECT_CONTAINS(str, "workspace ID 8 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/reload"));
|
|
||||||
|
|
||||||
Tests::killAllWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void testDynamicWsEffects() {
|
|
||||||
// test dynamic workspace effects, they shouldn't lag
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch workspace 69"));
|
|
||||||
|
|
||||||
Tests::spawnKitty("bitch");
|
|
||||||
|
|
||||||
OK(getFromSocket("r/keyword workspace 69,bordersize:20"));
|
|
||||||
OK(getFromSocket("r/keyword workspace 69,rounding:false"));
|
|
||||||
|
|
||||||
EXPECT(getFromSocket("/getprop class:bitch border_size"), "20");
|
|
||||||
EXPECT(getFromSocket("/getprop class:bitch rounding"), "0");
|
|
||||||
|
|
||||||
OK(getFromSocket("/reload"));
|
|
||||||
|
|
||||||
Tests::killAllWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool test() {
|
static bool test() {
|
||||||
NLog::log("{}Testing workspaces", Colors::GREEN);
|
NLog::log("{}Testing workspaces", Colors::GREEN);
|
||||||
|
|
||||||
|
|
@ -732,14 +594,13 @@ static bool test() {
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
|
|
||||||
testMultimonBAF();
|
testMultimonBAF();
|
||||||
testMultimonFocus();
|
|
||||||
|
|
||||||
// destroy the headless output
|
// destroy the headless output
|
||||||
OK(getFromSocket("/output remove HEADLESS-3"));
|
OK(getFromSocket("/output remove HEADLESS-3"));
|
||||||
|
|
||||||
testSpecialWorkspaceFullscreen();
|
testSpecialWorkspaceFullscreen();
|
||||||
|
|
||||||
testAsymmetricGaps();
|
testAsymmetricGaps();
|
||||||
testDynamicWsEffects();
|
|
||||||
|
|
||||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||||
EXPECT(Tests::windowCount(), 0);
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <fstream>
|
|
||||||
#include "../shared.hpp"
|
#include "../shared.hpp"
|
||||||
#include "../hyprctlCompat.hpp"
|
#include "../hyprctlCompat.hpp"
|
||||||
|
|
||||||
|
|
@ -40,38 +39,6 @@ CUniquePointer<CProcess> Tests::spawnKitty(const std::string& class_, const std:
|
||||||
return kitty;
|
return kitty;
|
||||||
}
|
}
|
||||||
|
|
||||||
CUniquePointer<CProcess> Tests::spawnLayerKitty(const std::string& namespace_, const std::vector<std::string> args) {
|
|
||||||
std::vector<std::string> programArgs = args;
|
|
||||||
if (!namespace_.empty()) {
|
|
||||||
programArgs.insert(programArgs.begin(), "--class");
|
|
||||||
programArgs.insert(programArgs.begin() + 1, namespace_);
|
|
||||||
}
|
|
||||||
|
|
||||||
programArgs.insert(programArgs.begin(), "+kitten");
|
|
||||||
programArgs.insert(programArgs.begin() + 1, "panel");
|
|
||||||
|
|
||||||
CUniquePointer<CProcess> kitty = makeUnique<CProcess>("kitty", programArgs);
|
|
||||||
kitty->addEnv("WAYLAND_DISPLAY", WLDISPLAY);
|
|
||||||
kitty->runAsync();
|
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
||||||
|
|
||||||
// wait while the layer spawns
|
|
||||||
int counter = 0;
|
|
||||||
while (processAlive(kitty->pid()) && countOccurrences(getFromSocket("/layers"), std::format("pid: {}", kitty->pid())) == 0) {
|
|
||||||
counter++;
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
|
|
||||||
if (counter > 50)
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!processAlive(kitty->pid()))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
return kitty;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Tests::processAlive(pid_t pid) {
|
bool Tests::processAlive(pid_t pid) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
int ret = kill(pid, 0);
|
int ret = kill(pid, 0);
|
||||||
|
|
@ -129,38 +96,6 @@ void Tests::waitUntilWindowsN(int n) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Tests::layerCount() {
|
|
||||||
return countOccurrences(getFromSocket("/layers"), "namespace: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Tests::killAllLayers() {
|
|
||||||
auto str = getFromSocket("/layers");
|
|
||||||
auto pos = str.find("pid: ");
|
|
||||||
while (pos != std::string::npos) {
|
|
||||||
auto pid = stoi(str.substr(pos + 5, str.find('\n', pos)));
|
|
||||||
kill(pid, 15);
|
|
||||||
|
|
||||||
// we need to wait for a bit because for some reason otherwise we'll end up
|
|
||||||
// with layers with pid -1 if they are all removed at the same time
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
|
|
||||||
pos = str.find("pid: ", pos + 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
int counter = 0;
|
|
||||||
while (Tests::layerCount() != 0) {
|
|
||||||
counter++;
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
|
|
||||||
if (counter > 50) {
|
|
||||||
std::println("{}Timed out waiting for layers to close", Colors::RED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Tests::execAndGet(const std::string& cmd) {
|
std::string Tests::execAndGet(const std::string& cmd) {
|
||||||
CProcess proc("/bin/sh", {"-c", cmd});
|
CProcess proc("/bin/sh", {"-c", cmd});
|
||||||
|
|
||||||
|
|
@ -170,14 +105,3 @@ std::string Tests::execAndGet(const std::string& cmd) {
|
||||||
|
|
||||||
return proc.stdOut();
|
return proc.stdOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Tests::writeFile(const std::string& name, const std::string& contents) {
|
|
||||||
std::ofstream of(name, std::ios::trunc);
|
|
||||||
if (!of.good())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
of << contents;
|
|
||||||
of.close();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,10 @@
|
||||||
//NOLINTNEXTLINE
|
//NOLINTNEXTLINE
|
||||||
namespace Tests {
|
namespace Tests {
|
||||||
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnKitty(const std::string& class_ = "", const std::vector<std::string> args = {});
|
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnKitty(const std::string& class_ = "", const std::vector<std::string> args = {});
|
||||||
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnLayerKitty(const std::string& namespace_ = "", const std::vector<std::string> args = {});
|
|
||||||
bool processAlive(pid_t pid);
|
bool processAlive(pid_t pid);
|
||||||
int windowCount();
|
int windowCount();
|
||||||
int countOccurrences(const std::string& in, const std::string& what);
|
int countOccurrences(const std::string& in, const std::string& what);
|
||||||
bool killAllWindows();
|
bool killAllWindows();
|
||||||
void waitUntilWindowsN(int n);
|
void waitUntilWindowsN(int n);
|
||||||
int layerCount();
|
|
||||||
bool killAllLayers();
|
|
||||||
std::string execAndGet(const std::string& cmd);
|
std::string execAndGet(const std::string& cmd);
|
||||||
bool writeFile(const std::string& name, const std::string& contents);
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -179,17 +179,6 @@ master {
|
||||||
new_status = master
|
new_status = master
|
||||||
}
|
}
|
||||||
|
|
||||||
scrolling {
|
|
||||||
fullscreen_on_one_column = true
|
|
||||||
column_width = 0.5
|
|
||||||
focus_fit_method = 1
|
|
||||||
follow_focus = true
|
|
||||||
follow_min_visible = 1
|
|
||||||
explicit_column_widths = 0.25, 0.333, 0.5, 0.667, 0.75, 1.0
|
|
||||||
wrap_focus = true
|
|
||||||
wrap_swapcol = true
|
|
||||||
}
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/Configuring/Variables/#misc
|
# https://wiki.hyprland.org/Configuring/Variables/#misc
|
||||||
misc {
|
misc {
|
||||||
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
|
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
|
||||||
|
|
@ -250,7 +239,7 @@ bind = $mainMod, E, exec, $fileManager
|
||||||
bind = $mainMod, V, togglefloating,
|
bind = $mainMod, V, togglefloating,
|
||||||
bind = $mainMod, R, exec, $menu
|
bind = $mainMod, R, exec, $menu
|
||||||
bind = $mainMod, P, pseudo, # dwindle
|
bind = $mainMod, P, pseudo, # dwindle
|
||||||
bind = $mainMod, J, layoutmsg, togglesplit, # dwindle
|
bind = $mainMod, J, togglesplit, # dwindle
|
||||||
|
|
||||||
# Move focus with mainMod + arrow keys
|
# Move focus with mainMod + arrow keys
|
||||||
bind = $mainMod, left, movefocus, l
|
bind = $mainMod, left, movefocus, l
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
epoll-shim,
|
epoll-shim,
|
||||||
git,
|
git,
|
||||||
glaze-hyprland,
|
glaze-hyprland,
|
||||||
glslang,
|
|
||||||
gtest,
|
gtest,
|
||||||
hyprcursor,
|
hyprcursor,
|
||||||
hyprgraphics,
|
hyprgraphics,
|
||||||
|
|
@ -22,7 +21,6 @@
|
||||||
hyprutils,
|
hyprutils,
|
||||||
hyprwayland-scanner,
|
hyprwayland-scanner,
|
||||||
hyprwire,
|
hyprwire,
|
||||||
lcms2,
|
|
||||||
libGL,
|
libGL,
|
||||||
libdrm,
|
libdrm,
|
||||||
libexecinfo,
|
libexecinfo,
|
||||||
|
|
@ -66,20 +64,8 @@
|
||||||
inherit (builtins) foldl' readFile;
|
inherit (builtins) foldl' readFile;
|
||||||
inherit (lib.asserts) assertMsg;
|
inherit (lib.asserts) assertMsg;
|
||||||
inherit (lib.attrsets) mapAttrsToList;
|
inherit (lib.attrsets) mapAttrsToList;
|
||||||
inherit
|
inherit (lib.lists) flatten concatLists optional optionals;
|
||||||
(lib.lists)
|
inherit (lib.strings) makeBinPath optionalString cmakeBool trim;
|
||||||
flatten
|
|
||||||
concatLists
|
|
||||||
optional
|
|
||||||
optionals
|
|
||||||
;
|
|
||||||
inherit
|
|
||||||
(lib.strings)
|
|
||||||
makeBinPath
|
|
||||||
optionalString
|
|
||||||
cmakeBool
|
|
||||||
trim
|
|
||||||
;
|
|
||||||
fs = lib.fileset;
|
fs = lib.fileset;
|
||||||
|
|
||||||
adapters = flatten [
|
adapters = flatten [
|
||||||
|
|
@ -91,8 +77,7 @@
|
||||||
in
|
in
|
||||||
assert assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
|
assert assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
|
||||||
assert assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
|
assert assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
|
||||||
assert assertMsg (!hidpiXWayland)
|
assert assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hypr.land/Configuring/XWayland";
|
||||||
"The option `hidpiXWayland` has been removed. Please refer https://wiki.hypr.land/Configuring/XWayland";
|
|
||||||
assert assertMsg (!legacyRenderer) "The option `legacyRenderer` has been removed. Legacy renderer is no longer supported.";
|
assert assertMsg (!legacyRenderer) "The option `legacyRenderer` has been removed. Legacy renderer is no longer supported.";
|
||||||
assert assertMsg (!withHyprtester) "The option `withHyprtester` has been removed. Hyprtester is always built now.";
|
assert assertMsg (!withHyprtester) "The option `withHyprtester` has been removed. Hyprtester is always built now.";
|
||||||
customStdenv.mkDerivation (finalAttrs: {
|
customStdenv.mkDerivation (finalAttrs: {
|
||||||
|
|
@ -105,29 +90,24 @@ in
|
||||||
fs.intersection
|
fs.intersection
|
||||||
# allows non-flake builds to only include files tracked by git
|
# allows non-flake builds to only include files tracked by git
|
||||||
(fs.gitTracked ../.)
|
(fs.gitTracked ../.)
|
||||||
(
|
(fs.unions (flatten [
|
||||||
fs.unions (flatten [
|
../assets/hyprland-portals.conf
|
||||||
../assets/hyprland-portals.conf
|
../assets/install
|
||||||
../assets/install
|
../hyprctl
|
||||||
../hyprctl
|
../hyprland.pc.in
|
||||||
../hyprland.pc.in
|
../hyprpm
|
||||||
../hyprpm
|
../LICENSE
|
||||||
../LICENSE
|
../protocols
|
||||||
../protocols
|
../src
|
||||||
../src
|
../start
|
||||||
../start
|
../systemd
|
||||||
../systemd
|
../VERSION
|
||||||
../VERSION
|
(fs.fileFilter (file: file.hasExt "1") ../docs)
|
||||||
(fs.fileFilter (file: file.hasExt "1") ../docs)
|
(fs.fileFilter (file: file.hasExt "conf" || file.hasExt "in") ../example)
|
||||||
(fs.fileFilter (file: file.hasExt "conf" || file.hasExt "in") ../example)
|
(fs.fileFilter (file: file.hasExt "sh") ../scripts)
|
||||||
(fs.fileFilter (file: file.hasExt "sh") ../scripts)
|
(fs.fileFilter (file: file.name == "CMakeLists.txt") ../.)
|
||||||
(fs.fileFilter (file: file.name == "CMakeLists.txt") ../.)
|
(optional withTests [../tests ../hyprtester])
|
||||||
(optional withTests [
|
]));
|
||||||
../tests
|
|
||||||
../hyprtester
|
|
||||||
])
|
|
||||||
])
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
postPatch = ''
|
postPatch = ''
|
||||||
|
|
@ -143,10 +123,7 @@ in
|
||||||
GIT_COMMITS = revCount;
|
GIT_COMMITS = revCount;
|
||||||
GIT_COMMIT_DATE = date;
|
GIT_COMMIT_DATE = date;
|
||||||
GIT_COMMIT_HASH = commit;
|
GIT_COMMIT_HASH = commit;
|
||||||
GIT_DIRTY =
|
GIT_DIRTY = if (commit == "") then "clean" else "dirty";
|
||||||
if (commit == "")
|
|
||||||
then "clean"
|
|
||||||
else "dirty";
|
|
||||||
GIT_TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}";
|
GIT_TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -174,7 +151,6 @@ in
|
||||||
cairo
|
cairo
|
||||||
git
|
git
|
||||||
glaze-hyprland
|
glaze-hyprland
|
||||||
glslang
|
|
||||||
gtest
|
gtest
|
||||||
hyprcursor
|
hyprcursor
|
||||||
hyprgraphics
|
hyprgraphics
|
||||||
|
|
@ -182,7 +158,6 @@ in
|
||||||
hyprlang
|
hyprlang
|
||||||
hyprutils
|
hyprutils
|
||||||
hyprwire
|
hyprwire
|
||||||
lcms2
|
|
||||||
libdrm
|
libdrm
|
||||||
libgbm
|
libgbm
|
||||||
libGL
|
libGL
|
||||||
|
|
@ -243,14 +218,12 @@ in
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
${optionalString wrapRuntimeDeps ''
|
${optionalString wrapRuntimeDeps ''
|
||||||
wrapProgram $out/bin/Hyprland \
|
wrapProgram $out/bin/Hyprland \
|
||||||
--suffix PATH : ${
|
--suffix PATH : ${makeBinPath [
|
||||||
makeBinPath [
|
binutils
|
||||||
binutils
|
hyprland-guiutils
|
||||||
hyprland-guiutils
|
pciutils
|
||||||
pciutils
|
pkgconf
|
||||||
pkgconf
|
]}
|
||||||
]
|
|
||||||
}
|
|
||||||
''}
|
''}
|
||||||
|
|
||||||
${optionalString withTests ''
|
${optionalString withTests ''
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
writeShellApplication,
|
writeShellApplication,
|
||||||
deadnix,
|
deadnix,
|
||||||
statix,
|
statix,
|
||||||
nixfmt,
|
alejandra,
|
||||||
llvmPackages_19,
|
llvmPackages_19,
|
||||||
fd,
|
fd,
|
||||||
}:
|
}:
|
||||||
|
|
@ -11,7 +11,7 @@ writeShellApplication {
|
||||||
runtimeInputs = [
|
runtimeInputs = [
|
||||||
deadnix
|
deadnix
|
||||||
statix
|
statix
|
||||||
nixfmt
|
alejandra
|
||||||
llvmPackages_19.clang-tools
|
llvmPackages_19.clang-tools
|
||||||
fd
|
fd
|
||||||
];
|
];
|
||||||
|
|
@ -24,14 +24,14 @@ writeShellApplication {
|
||||||
nix_format() {
|
nix_format() {
|
||||||
if [ "$*" = 0 ]; then
|
if [ "$*" = 0 ]; then
|
||||||
fd '.*\.nix' . -E "$excludes" -x statix fix -- {} \;
|
fd '.*\.nix' . -E "$excludes" -x statix fix -- {} \;
|
||||||
fd '.*\.nix' . -E "$excludes" -X deadnix -e -- {} \; -X nixfmt {} \;
|
fd '.*\.nix' . -E "$excludes" -X deadnix -e -- {} \; -X alejandra {} \;
|
||||||
elif [ -d "$1" ]; then
|
elif [ -d "$1" ]; then
|
||||||
fd '.*\.nix' "$1" -E "$excludes" -i -x statix fix -- {} \;
|
fd '.*\.nix' "$1" -E "$excludes" -i -x statix fix -- {} \;
|
||||||
fd '.*\.nix' "$1" -E "$excludes" -i -X deadnix -e -- {} \; -X nixfmt {} \;
|
fd '.*\.nix' "$1" -E "$excludes" -i -X deadnix -e -- {} \; -X alejandra {} \;
|
||||||
else
|
else
|
||||||
statix fix -- "$1"
|
statix fix -- "$1"
|
||||||
deadnix -e "$1"
|
deadnix -e "$1"
|
||||||
nixfmt "$1"
|
alejandra "$1"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
self:
|
self: {
|
||||||
{
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}: let
|
||||||
let
|
|
||||||
inherit (pkgs.stdenv.hostPlatform) system;
|
inherit (pkgs.stdenv.hostPlatform) system;
|
||||||
|
|
||||||
package = self.packages.${system}.default;
|
package = self.packages.${system}.default;
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
config = {
|
config = {
|
||||||
wayland.windowManager.hyprland.package = lib.mkDefault package;
|
wayland.windowManager.hyprland.package = lib.mkDefault package;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
115
nix/lib.nix
115
nix/lib.nix
|
|
@ -1,5 +1,4 @@
|
||||||
lib:
|
lib: let
|
||||||
let
|
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
attrNames
|
attrNames
|
||||||
filterAttrs
|
filterAttrs
|
||||||
|
|
@ -18,7 +17,7 @@ let
|
||||||
|
|
||||||
This function takes a nested attribute set and converts it into Hyprland-compatible
|
This function takes a nested attribute set and converts it into Hyprland-compatible
|
||||||
configuration syntax, supporting top, bottom, and regular command sections.
|
configuration syntax, supporting top, bottom, and regular command sections.
|
||||||
|
|
||||||
Commands are flattened using the `flattenAttrs` function, and attributes are formatted as
|
Commands are flattened using the `flattenAttrs` function, and attributes are formatted as
|
||||||
`key = value` pairs. Lists are expanded as duplicate keys to match Hyprland's expected format.
|
`key = value` pairs. Lists are expanded as duplicate keys to match Hyprland's expected format.
|
||||||
|
|
||||||
|
|
@ -82,51 +81,44 @@ let
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
toHyprlang =
|
toHyprlang = {
|
||||||
{
|
topCommandsPrefixes ? ["$" "bezier"],
|
||||||
topCommandsPrefixes ? [
|
bottomCommandsPrefixes ? [],
|
||||||
"$"
|
}: attrs: let
|
||||||
"bezier"
|
toHyprlang' = attrs: let
|
||||||
],
|
# Specially configured `toKeyValue` generator with support for duplicate keys
|
||||||
bottomCommandsPrefixes ? [ ],
|
# and a legible key-value separator.
|
||||||
}:
|
mkCommands = generators.toKeyValue {
|
||||||
attrs:
|
mkKeyValue = generators.mkKeyValueDefault {} " = ";
|
||||||
let
|
listsAsDuplicateKeys = true;
|
||||||
toHyprlang' =
|
indent = ""; # No indent, since we don't have nesting
|
||||||
attrs:
|
};
|
||||||
let
|
|
||||||
# Specially configured `toKeyValue` generator with support for duplicate keys
|
|
||||||
# and a legible key-value separator.
|
|
||||||
mkCommands = generators.toKeyValue {
|
|
||||||
mkKeyValue = generators.mkKeyValueDefault { } " = ";
|
|
||||||
listsAsDuplicateKeys = true;
|
|
||||||
indent = ""; # No indent, since we don't have nesting
|
|
||||||
};
|
|
||||||
|
|
||||||
# Flatten the attrset, combining keys in a "path" like `"a:b:c" = "x"`.
|
# Flatten the attrset, combining keys in a "path" like `"a:b:c" = "x"`.
|
||||||
# Uses `flattenAttrs` with a colon separator.
|
# Uses `flattenAttrs` with a colon separator.
|
||||||
commands = flattenAttrs (p: k: "${p}:${k}") attrs;
|
commands = flattenAttrs (p: k: "${p}:${k}") attrs;
|
||||||
|
|
||||||
# General filtering function to check if a key starts with any prefix in a given list.
|
# General filtering function to check if a key starts with any prefix in a given list.
|
||||||
filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list;
|
filterCommands = list: n:
|
||||||
|
foldl (acc: prefix: acc || hasPrefix prefix n) false list;
|
||||||
|
|
||||||
# Partition keys into top commands and the rest
|
# Partition keys into top commands and the rest
|
||||||
result = partition (filterCommands topCommandsPrefixes) (attrNames commands);
|
result = partition (filterCommands topCommandsPrefixes) (attrNames commands);
|
||||||
topCommands = filterAttrs (n: _: builtins.elem n result.right) commands;
|
topCommands = filterAttrs (n: _: builtins.elem n result.right) commands;
|
||||||
remainingCommands = removeAttrs commands result.right;
|
remainingCommands = removeAttrs commands result.right;
|
||||||
|
|
||||||
# Partition remaining commands into bottom commands and regular commands
|
# Partition remaining commands into bottom commands and regular commands
|
||||||
result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong;
|
result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong;
|
||||||
bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands;
|
bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands;
|
||||||
regularCommands = removeAttrs remainingCommands result2.right;
|
regularCommands = removeAttrs remainingCommands result2.right;
|
||||||
in
|
|
||||||
# Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands.
|
|
||||||
concatMapStrings mkCommands [
|
|
||||||
topCommands
|
|
||||||
regularCommands
|
|
||||||
bottomCommands
|
|
||||||
];
|
|
||||||
in
|
in
|
||||||
|
# Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands.
|
||||||
|
concatMapStrings mkCommands [
|
||||||
|
topCommands
|
||||||
|
regularCommands
|
||||||
|
bottomCommands
|
||||||
|
];
|
||||||
|
in
|
||||||
toHyprlang' attrs;
|
toHyprlang' attrs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -139,7 +131,7 @@ let
|
||||||
Configuration:
|
Configuration:
|
||||||
|
|
||||||
* `pred` - A function `(string -> string -> string)` defining how keys should be concatenated.
|
* `pred` - A function `(string -> string -> string)` defining how keys should be concatenated.
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
Structured function argument:
|
Structured function argument:
|
||||||
|
|
@ -147,7 +139,7 @@ let
|
||||||
: pred (required)
|
: pred (required)
|
||||||
: A function that determines how parent and child keys should be combined into a single key.
|
: A function that determines how parent and child keys should be combined into a single key.
|
||||||
It takes a `prefix` (parent key) and `key` (current key) and returns the joined key.
|
It takes a `prefix` (parent key) and `key` (current key) and returns the joined key.
|
||||||
|
|
||||||
Value:
|
Value:
|
||||||
|
|
||||||
: The nested attribute set to be flattened.
|
: The nested attribute set to be flattened.
|
||||||
|
|
@ -182,21 +174,26 @@ let
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
*/
|
*/
|
||||||
flattenAttrs =
|
flattenAttrs = pred: attrs: let
|
||||||
pred: attrs:
|
flattenAttrs' = prefix: attrs:
|
||||||
let
|
builtins.foldl' (
|
||||||
flattenAttrs' =
|
acc: key: let
|
||||||
prefix: attrs:
|
value = attrs.${key};
|
||||||
builtins.foldl' (
|
newKey =
|
||||||
acc: key:
|
if prefix == ""
|
||||||
let
|
then key
|
||||||
value = attrs.${key};
|
else pred prefix key;
|
||||||
newKey = if prefix == "" then key else pred prefix key;
|
in
|
||||||
in
|
acc
|
||||||
acc // (if builtins.isAttrs value then flattenAttrs' newKey value else { "${newKey}" = value; })
|
// (
|
||||||
) { } (builtins.attrNames attrs);
|
if builtins.isAttrs value
|
||||||
in
|
then flattenAttrs' newKey value
|
||||||
|
else {"${newKey}" = value;}
|
||||||
|
)
|
||||||
|
) {} (builtins.attrNames attrs);
|
||||||
|
in
|
||||||
flattenAttrs' "" attrs;
|
flattenAttrs' "" attrs;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
|
||||||
111
nix/module.nix
111
nix/module.nix
|
|
@ -1,21 +1,18 @@
|
||||||
inputs:
|
inputs: {
|
||||||
{
|
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}: let
|
||||||
let
|
|
||||||
inherit (pkgs.stdenv.hostPlatform) system;
|
inherit (pkgs.stdenv.hostPlatform) system;
|
||||||
selflib = import ./lib.nix lib;
|
selflib = import ./lib.nix lib;
|
||||||
cfg = config.programs.hyprland;
|
cfg = config.programs.hyprland;
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
options = {
|
options = {
|
||||||
programs.hyprland = {
|
programs.hyprland = {
|
||||||
plugins = lib.mkOption {
|
plugins = lib.mkOption {
|
||||||
type = with lib.types; listOf (either package path);
|
type = with lib.types; listOf (either package path);
|
||||||
default = [ ];
|
default = [];
|
||||||
description = ''
|
description = ''
|
||||||
List of Hyprland plugins to use. Can either be packages or
|
List of Hyprland plugins to use. Can either be packages or
|
||||||
absolute plugin paths.
|
absolute plugin paths.
|
||||||
|
|
@ -23,25 +20,23 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
settings = lib.mkOption {
|
settings = lib.mkOption {
|
||||||
type =
|
type = with lib.types; let
|
||||||
with lib.types;
|
valueType =
|
||||||
let
|
nullOr (oneOf [
|
||||||
valueType =
|
bool
|
||||||
nullOr (oneOf [
|
int
|
||||||
bool
|
float
|
||||||
int
|
str
|
||||||
float
|
path
|
||||||
str
|
(attrsOf valueType)
|
||||||
path
|
(listOf valueType)
|
||||||
(attrsOf valueType)
|
])
|
||||||
(listOf valueType)
|
// {
|
||||||
])
|
description = "Hyprland configuration value";
|
||||||
// {
|
};
|
||||||
description = "Hyprland configuration value";
|
in
|
||||||
};
|
|
||||||
in
|
|
||||||
valueType;
|
valueType;
|
||||||
default = { };
|
default = {};
|
||||||
description = ''
|
description = ''
|
||||||
Hyprland configuration written in Nix. Entries with the same key
|
Hyprland configuration written in Nix. Entries with the same key
|
||||||
should be written as lists. Variables' and colors' names should be
|
should be written as lists. Variables' and colors' names should be
|
||||||
|
|
@ -97,15 +92,8 @@ in
|
||||||
|
|
||||||
topPrefixes = lib.mkOption {
|
topPrefixes = lib.mkOption {
|
||||||
type = with lib.types; listOf str;
|
type = with lib.types; listOf str;
|
||||||
default = [
|
default = ["$" "bezier"];
|
||||||
"$"
|
example = ["$" "bezier" "source"];
|
||||||
"bezier"
|
|
||||||
];
|
|
||||||
example = [
|
|
||||||
"$"
|
|
||||||
"bezier"
|
|
||||||
"source"
|
|
||||||
];
|
|
||||||
description = ''
|
description = ''
|
||||||
List of prefix of attributes to put at the top of the config.
|
List of prefix of attributes to put at the top of the config.
|
||||||
'';
|
'';
|
||||||
|
|
@ -113,8 +101,8 @@ in
|
||||||
|
|
||||||
bottomPrefixes = lib.mkOption {
|
bottomPrefixes = lib.mkOption {
|
||||||
type = with lib.types; listOf str;
|
type = with lib.types; listOf str;
|
||||||
default = [ ];
|
default = [];
|
||||||
example = [ "source" ];
|
example = ["source"];
|
||||||
description = ''
|
description = ''
|
||||||
List of prefix of attributes to put at the bottom of the config.
|
List of prefix of attributes to put at the bottom of the config.
|
||||||
'';
|
'';
|
||||||
|
|
@ -129,36 +117,35 @@ in
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(lib.mkIf cfg.enable {
|
(lib.mkIf cfg.enable {
|
||||||
environment.etc."xdg/hypr/hyprland.conf" =
|
environment.etc."xdg/hypr/hyprland.conf" = let
|
||||||
let
|
shouldGenerate = cfg.extraConfig != "" || cfg.settings != {} || cfg.plugins != [];
|
||||||
shouldGenerate = cfg.extraConfig != "" || cfg.settings != { } || cfg.plugins != [ ];
|
|
||||||
|
|
||||||
pluginsToHyprlang =
|
pluginsToHyprlang = plugins:
|
||||||
_plugins:
|
selflib.toHyprlang {
|
||||||
selflib.toHyprlang
|
topCommandsPrefixes = cfg.topPrefixes;
|
||||||
{
|
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"exec-once" = let
|
||||||
|
mkEntry = entry:
|
||||||
|
if lib.types.package.check entry
|
||||||
|
then "${entry}/lib/lib${entry.pname}.so"
|
||||||
|
else entry;
|
||||||
|
hyprctl = lib.getExe' config.programs.hyprland.package "hyprctl";
|
||||||
|
in
|
||||||
|
map (p: "${hyprctl} plugin load ${mkEntry p}") cfg.plugins;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
lib.mkIf shouldGenerate {
|
||||||
|
text =
|
||||||
|
lib.optionalString (cfg.plugins != [])
|
||||||
|
(pluginsToHyprlang cfg.plugins)
|
||||||
|
+ lib.optionalString (cfg.settings != {})
|
||||||
|
(selflib.toHyprlang {
|
||||||
topCommandsPrefixes = cfg.topPrefixes;
|
topCommandsPrefixes = cfg.topPrefixes;
|
||||||
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
||||||
}
|
}
|
||||||
{
|
cfg.settings)
|
||||||
"exec-once" =
|
|
||||||
let
|
|
||||||
mkEntry =
|
|
||||||
entry: if lib.types.package.check entry then "${entry}/lib/lib${entry.pname}.so" else entry;
|
|
||||||
hyprctl = lib.getExe' config.programs.hyprland.package "hyprctl";
|
|
||||||
in
|
|
||||||
map (p: "${hyprctl} plugin load ${mkEntry p}") cfg.plugins;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
lib.mkIf shouldGenerate {
|
|
||||||
text =
|
|
||||||
lib.optionalString (cfg.plugins != [ ]) (pluginsToHyprlang cfg.plugins)
|
|
||||||
+ lib.optionalString (cfg.settings != { }) (
|
|
||||||
selflib.toHyprlang {
|
|
||||||
topCommandsPrefixes = cfg.topPrefixes;
|
|
||||||
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
|
||||||
} cfg.settings
|
|
||||||
)
|
|
||||||
+ lib.optionalString (cfg.extraConfig != "") cfg.extraConfig;
|
+ lib.optionalString (cfg.extraConfig != "") cfg.extraConfig;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
|
||||||
115
nix/overlays.nix
115
nix/overlays.nix
|
|
@ -2,27 +2,20 @@
|
||||||
self,
|
self,
|
||||||
lib,
|
lib,
|
||||||
inputs,
|
inputs,
|
||||||
}:
|
}: let
|
||||||
let
|
mkDate = longDate: (lib.concatStringsSep "-" [
|
||||||
mkDate =
|
(builtins.substring 0 4 longDate)
|
||||||
longDate:
|
(builtins.substring 4 2 longDate)
|
||||||
(lib.concatStringsSep "-" [
|
(builtins.substring 6 2 longDate)
|
||||||
(builtins.substring 0 4 longDate)
|
]);
|
||||||
(builtins.substring 4 2 longDate)
|
|
||||||
(builtins.substring 6 2 longDate)
|
|
||||||
]);
|
|
||||||
ver = lib.removeSuffix "\n" (builtins.readFile ../VERSION);
|
ver = lib.removeSuffix "\n" (builtins.readFile ../VERSION);
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
# Contains what a user is most likely to care about:
|
# Contains what a user is most likely to care about:
|
||||||
# Hyprland itself, XDPH and the Share Picker.
|
# Hyprland itself, XDPH and the Share Picker.
|
||||||
default = lib.composeManyExtensions (
|
default = lib.composeManyExtensions (with self.overlays; [
|
||||||
with self.overlays;
|
hyprland-packages
|
||||||
[
|
hyprland-extras
|
||||||
hyprland-packages
|
]);
|
||||||
hyprland-extras
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
# Packages for variations of Hyprland, dependencies included.
|
# Packages for variations of Hyprland, dependencies included.
|
||||||
hyprland-packages = lib.composeManyExtensions [
|
hyprland-packages = lib.composeManyExtensions [
|
||||||
|
|
@ -40,45 +33,49 @@ in
|
||||||
self.overlays.glaze
|
self.overlays.glaze
|
||||||
|
|
||||||
# Hyprland packages themselves
|
# Hyprland packages themselves
|
||||||
(
|
(final: _prev: let
|
||||||
final: _prev:
|
date = mkDate (self.lastModifiedDate or "19700101");
|
||||||
let
|
version = "${ver}+date=${date}_${self.shortRev or "dirty"}";
|
||||||
date = mkDate (self.lastModifiedDate or "19700101");
|
in {
|
||||||
version = "${ver}+date=${date}_${self.shortRev or "dirty"}";
|
hyprland = final.callPackage ./default.nix {
|
||||||
in
|
stdenv = final.gcc15Stdenv;
|
||||||
{
|
commit = self.rev or "";
|
||||||
hyprland = final.callPackage ./default.nix {
|
revCount = self.sourceInfo.revCount or "";
|
||||||
stdenv = final.gcc15Stdenv;
|
inherit date version;
|
||||||
commit = self.rev or "";
|
};
|
||||||
revCount = self.sourceInfo.revCount or "";
|
hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;};
|
||||||
inherit date version;
|
|
||||||
};
|
|
||||||
hyprland-unwrapped = final.hyprland.override { wrapRuntimeDeps = false; };
|
|
||||||
|
|
||||||
hyprland-with-tests = final.hyprland.override { withTests = true; };
|
hyprland-with-tests = final.hyprland.override {withTests = true;};
|
||||||
|
|
||||||
hyprland-with-hyprtester = builtins.trace ''
|
hyprland-with-hyprtester =
|
||||||
|
builtins.trace ''
|
||||||
hyprland-with-hyprtester was removed. Please use the hyprland package.
|
hyprland-with-hyprtester was removed. Please use the hyprland package.
|
||||||
Hyprtester is always built now.
|
Hyprtester is always built now.
|
||||||
'' final.hyprland;
|
''
|
||||||
|
final.hyprland;
|
||||||
|
|
||||||
# deprecated packages
|
# deprecated packages
|
||||||
hyprland-legacy-renderer = builtins.trace ''
|
hyprland-legacy-renderer =
|
||||||
|
builtins.trace ''
|
||||||
hyprland-legacy-renderer was removed. Please use the hyprland package.
|
hyprland-legacy-renderer was removed. Please use the hyprland package.
|
||||||
Legacy renderer is no longer supported.
|
Legacy renderer is no longer supported.
|
||||||
'' final.hyprland;
|
''
|
||||||
|
final.hyprland;
|
||||||
|
|
||||||
hyprland-nvidia = builtins.trace ''
|
hyprland-nvidia =
|
||||||
|
builtins.trace ''
|
||||||
hyprland-nvidia was removed. Please use the hyprland package.
|
hyprland-nvidia was removed. Please use the hyprland package.
|
||||||
Nvidia patches are no longer needed.
|
Nvidia patches are no longer needed.
|
||||||
'' final.hyprland;
|
''
|
||||||
|
final.hyprland;
|
||||||
|
|
||||||
hyprland-hidpi = builtins.trace ''
|
hyprland-hidpi =
|
||||||
|
builtins.trace ''
|
||||||
hyprland-hidpi was removed. Please use the hyprland package.
|
hyprland-hidpi was removed. Please use the hyprland package.
|
||||||
For more information, refer to https://wiki.hypr.land/Configuring/XWayland.
|
For more information, refer to https://wiki.hypr.land/Configuring/XWayland.
|
||||||
'' final.hyprland;
|
''
|
||||||
}
|
final.hyprland;
|
||||||
)
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
|
|
@ -86,10 +83,10 @@ in
|
||||||
# Dependencies
|
# Dependencies
|
||||||
self.overlays.hyprland-packages
|
self.overlays.hyprland-packages
|
||||||
|
|
||||||
(_final: prev: {
|
(final: prev: {
|
||||||
aquamarine = prev.aquamarine.override { debug = true; };
|
aquamarine = prev.aquamarine.override {debug = true;};
|
||||||
hyprutils = prev.hyprutils.override { debug = true; };
|
hyprutils = prev.hyprutils.override {debug = true;};
|
||||||
hyprland-debug = prev.hyprland.override { debug = true; };
|
hyprland-debug = prev.hyprland.override {debug = true;};
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -103,23 +100,21 @@ in
|
||||||
# this version is the one used in the git submodule, and allows us to
|
# this version is the one used in the git submodule, and allows us to
|
||||||
# fetch the source without '?submodules=1'
|
# fetch the source without '?submodules=1'
|
||||||
udis86 = final: prev: {
|
udis86 = final: prev: {
|
||||||
udis86-hyprland = prev.udis86.overrideAttrs (
|
udis86-hyprland = prev.udis86.overrideAttrs (_self: _super: {
|
||||||
_self: _super: {
|
src = final.fetchFromGitHub {
|
||||||
src = final.fetchFromGitHub {
|
owner = "canihavesomecoffee";
|
||||||
owner = "canihavesomecoffee";
|
repo = "udis86";
|
||||||
repo = "udis86";
|
rev = "5336633af70f3917760a6d441ff02d93477b0c86";
|
||||||
rev = "5336633af70f3917760a6d441ff02d93477b0c86";
|
hash = "sha256-HifdUQPGsKQKQprByeIznvRLONdOXeolOsU5nkwIv3g=";
|
||||||
hash = "sha256-HifdUQPGsKQKQprByeIznvRLONdOXeolOsU5nkwIv3g=";
|
};
|
||||||
};
|
|
||||||
|
|
||||||
patches = [ ];
|
patches = [];
|
||||||
}
|
});
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Even though glaze itself disables it by default, nixpkgs sets ENABLE_SSL set to true.
|
# Even though glaze itself disables it by default, nixpkgs sets ENABLE_SSL set to true.
|
||||||
# Since we don't include openssl, the build failes without the `enableSSL = false;` override
|
# Since we don't include openssl, the build failes without the `enableSSL = false;` override
|
||||||
glaze = _final: prev: {
|
glaze = final: prev: {
|
||||||
glaze-hyprland = prev.glaze.override {
|
glaze-hyprland = prev.glaze.override {
|
||||||
enableSSL = false;
|
enableSSL = false;
|
||||||
enableInterop = false;
|
enableInterop = false;
|
||||||
|
|
|
||||||
|
|
@ -1,76 +1,72 @@
|
||||||
inputs: pkgs:
|
inputs: pkgs: let
|
||||||
let
|
|
||||||
flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||||
hyprland = flake.hyprland-with-tests;
|
hyprland = flake.hyprland-with-tests;
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
tests = pkgs.testers.runNixOSTest {
|
tests = pkgs.testers.runNixOSTest {
|
||||||
name = "hyprland-tests";
|
name = "hyprland-tests";
|
||||||
|
|
||||||
nodes.machine =
|
nodes.machine = {pkgs, ...}: {
|
||||||
{ pkgs, ... }:
|
environment.systemPackages = with pkgs; [
|
||||||
{
|
# Programs needed for tests
|
||||||
environment.systemPackages = with pkgs; [
|
jq
|
||||||
# Programs needed for tests
|
kitty
|
||||||
jq
|
wl-clipboard
|
||||||
kitty
|
xeyes
|
||||||
wl-clipboard
|
];
|
||||||
xeyes
|
|
||||||
];
|
|
||||||
|
|
||||||
# Enabled by default for some reason
|
# Enabled by default for some reason
|
||||||
services.speechd.enable = false;
|
services.speechd.enable = false;
|
||||||
|
|
||||||
environment.variables = {
|
environment.variables = {
|
||||||
"AQ_TRACE" = "1";
|
"AQ_TRACE" = "1";
|
||||||
"HYPRLAND_TRACE" = "1";
|
"HYPRLAND_TRACE" = "1";
|
||||||
"XDG_RUNTIME_DIR" = "/tmp";
|
"XDG_RUNTIME_DIR" = "/tmp";
|
||||||
"XDG_CACHE_HOME" = "/tmp";
|
"XDG_CACHE_HOME" = "/tmp";
|
||||||
"KITTY_CONFIG_DIRECTORY" = "/etc/kitty";
|
"KITTY_CONFIG_DIRECTORY" = "/etc/kitty";
|
||||||
};
|
|
||||||
|
|
||||||
environment.etc."kitty/kitty.conf".text = ''
|
|
||||||
confirm_os_window_close 0
|
|
||||||
remember_window_size no
|
|
||||||
initial_window_width 640
|
|
||||||
initial_window_height 400
|
|
||||||
'';
|
|
||||||
|
|
||||||
programs.hyprland = {
|
|
||||||
enable = true;
|
|
||||||
package = hyprland;
|
|
||||||
# We don't need portals in this test, so we don't set portalPackage
|
|
||||||
};
|
|
||||||
|
|
||||||
# Test configuration
|
|
||||||
environment.etc."test.conf".source = "${hyprland}/share/hypr/test.conf";
|
|
||||||
|
|
||||||
# Disable portals
|
|
||||||
xdg.portal.enable = pkgs.lib.mkForce false;
|
|
||||||
|
|
||||||
# Autologin root into tty
|
|
||||||
services.getty.autologinUser = "alice";
|
|
||||||
|
|
||||||
system.stateVersion = "24.11";
|
|
||||||
|
|
||||||
users.users.alice = {
|
|
||||||
isNormalUser = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation = {
|
|
||||||
cores = 4;
|
|
||||||
# Might crash with less
|
|
||||||
memorySize = 8192;
|
|
||||||
resolution = {
|
|
||||||
x = 1920;
|
|
||||||
y = 1080;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Doesn't seem to do much, thought it would fix XWayland crashing
|
|
||||||
qemu.options = [ "-vga none -device virtio-gpu-pci" ];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
environment.etc."kitty/kitty.conf".text = ''
|
||||||
|
confirm_os_window_close 0
|
||||||
|
remember_window_size no
|
||||||
|
initial_window_width 640
|
||||||
|
initial_window_height 400
|
||||||
|
'';
|
||||||
|
|
||||||
|
programs.hyprland = {
|
||||||
|
enable = true;
|
||||||
|
package = hyprland;
|
||||||
|
# We don't need portals in this test, so we don't set portalPackage
|
||||||
|
};
|
||||||
|
|
||||||
|
# Test configuration
|
||||||
|
environment.etc."test.conf".source = "${hyprland}/share/hypr/test.conf";
|
||||||
|
|
||||||
|
# Disable portals
|
||||||
|
xdg.portal.enable = pkgs.lib.mkForce false;
|
||||||
|
|
||||||
|
# Autologin root into tty
|
||||||
|
services.getty.autologinUser = "alice";
|
||||||
|
|
||||||
|
system.stateVersion = "24.11";
|
||||||
|
|
||||||
|
users.users.alice = {
|
||||||
|
isNormalUser = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualisation = {
|
||||||
|
cores = 4;
|
||||||
|
# Might crash with less
|
||||||
|
memorySize = 8192;
|
||||||
|
resolution = {
|
||||||
|
x = 1920;
|
||||||
|
y = 1080;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Doesn't seem to do much, thought it would fix XWayland crashing
|
||||||
|
qemu.options = ["-vga none -device virtio-gpu-pci"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
testScript = ''
|
testScript = ''
|
||||||
# Wait for tty to be up
|
# Wait for tty to be up
|
||||||
machine.wait_for_unit("multi-user.target")
|
machine.wait_for_unit("multi-user.target")
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ echo 'static const std::map<std::string, std::string> SHADERS = {' >> ./src/rend
|
||||||
for filename in `ls ${SHADERS_SRC}`; do
|
for filename in `ls ${SHADERS_SRC}`; do
|
||||||
echo "-- ${filename}"
|
echo "-- ${filename}"
|
||||||
|
|
||||||
{ echo -n 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc
|
{ echo 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc
|
||||||
echo "{\"${filename}\"," >> ./src/render/shaders/Shaders.hpp
|
echo "{\"${filename}\"," >> ./src/render/shaders/Shaders.hpp
|
||||||
echo "#include \"./${filename}.inc\"" >> ./src/render/shaders/Shaders.hpp
|
echo "#include \"./${filename}.inc\"" >> ./src/render/shaders/Shaders.hpp
|
||||||
echo "}," >> ./src/render/shaders/Shaders.hpp
|
echo "}," >> ./src/render/shaders/Shaders.hpp
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@
|
||||||
#include "protocols/core/Compositor.hpp"
|
#include "protocols/core/Compositor.hpp"
|
||||||
#include "protocols/core/Subcompositor.hpp"
|
#include "protocols/core/Subcompositor.hpp"
|
||||||
#include "desktop/view/LayerSurface.hpp"
|
#include "desktop/view/LayerSurface.hpp"
|
||||||
#include "layout/space/Space.hpp"
|
|
||||||
#include "render/Renderer.hpp"
|
#include "render/Renderer.hpp"
|
||||||
#include "xwayland/XWayland.hpp"
|
#include "xwayland/XWayland.hpp"
|
||||||
#include "helpers/ByteOperations.hpp"
|
#include "helpers/ByteOperations.hpp"
|
||||||
|
|
@ -1405,35 +1404,6 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks
|
||||||
PHLWINDOW leaderWindow = nullptr;
|
PHLWINDOW leaderWindow = nullptr;
|
||||||
|
|
||||||
if (!useVectorAngles) {
|
if (!useVectorAngles) {
|
||||||
// helper to check if two rectangles are adjacent along an axis, considering slight overlaps.
|
|
||||||
// returns true if: STICKS (delta <= 2) OR rectangles overlap but no more than 50% of the smaller dimension.
|
|
||||||
static auto isAdjacent = [](const double aMin, const double aMax, const double bMin, const double bMax) -> bool {
|
|
||||||
constexpr double STICK_THRESHOLD = 2.0;
|
|
||||||
constexpr double MAX_OVERLAP_RATIO = 0.5;
|
|
||||||
|
|
||||||
const double aEdge = aMin;
|
|
||||||
const double bEdge = bMax;
|
|
||||||
const double delta = aEdge - bEdge;
|
|
||||||
|
|
||||||
// old STICKS check for 2px
|
|
||||||
if (std::abs(delta) < STICK_THRESHOLD)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (delta >= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const double overlap = -delta;
|
|
||||||
const double sizeA = aMax - aMin;
|
|
||||||
const double sizeB = bMax - bMin;
|
|
||||||
|
|
||||||
// reject if one rectangle fully contains the other
|
|
||||||
if ((bMin <= aMin && bMax >= aMax) || (aMin <= bMin && aMax >= bMax))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// accept if overlap is at most 50% of the smaller dimension
|
|
||||||
return overlap <= std::min(sizeA, sizeB) * MAX_OVERLAP_RATIO;
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto const& w : m_windows) {
|
for (auto const& w : m_windows) {
|
||||||
if (w == ignoreWindow || !w->m_workspace || !w->m_isMapped || w->isHidden() || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible())
|
if (w == ignoreWindow || !w->m_workspace || !w->m_isMapped || w->isHidden() || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible())
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1456,20 +1426,24 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks
|
||||||
|
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
case Math::DIRECTION_LEFT:
|
case Math::DIRECTION_LEFT:
|
||||||
if (isAdjacent(POSA.x, POSA.x + SIZEA.x, POSB.x, POSB.x + SIZEB.x))
|
if (STICKS(POSA.x, POSB.x + SIZEB.x)) {
|
||||||
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Math::DIRECTION_RIGHT:
|
case Math::DIRECTION_RIGHT:
|
||||||
if (isAdjacent(POSB.x, POSB.x + SIZEB.x, POSA.x, POSA.x + SIZEA.x))
|
if (STICKS(POSA.x + SIZEA.x, POSB.x)) {
|
||||||
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Math::DIRECTION_UP:
|
case Math::DIRECTION_UP:
|
||||||
if (isAdjacent(POSA.y, POSA.y + SIZEA.y, POSB.y, POSB.y + SIZEB.y))
|
if (STICKS(POSA.y, POSB.y + SIZEB.y)) {
|
||||||
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Math::DIRECTION_DOWN:
|
case Math::DIRECTION_DOWN:
|
||||||
if (isAdjacent(POSB.y, POSB.y + SIZEB.y, POSA.y, POSA.y + SIZEA.y))
|
if (STICKS(POSA.y + SIZEA.y, POSB.y)) {
|
||||||
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
@ -1825,9 +1799,6 @@ void CCompositor::swapActiveWorkspaces(PHLMONITOR pMonitorA, PHLMONITOR pMonitor
|
||||||
g_layoutManager->recalculateMonitor(pMonitorA);
|
g_layoutManager->recalculateMonitor(pMonitorA);
|
||||||
g_layoutManager->recalculateMonitor(pMonitorB);
|
g_layoutManager->recalculateMonitor(pMonitorB);
|
||||||
|
|
||||||
g_pHyprRenderer->damageMonitor(pMonitorB);
|
|
||||||
g_pHyprRenderer->damageMonitor(pMonitorA);
|
|
||||||
|
|
||||||
g_pDesktopAnimationManager->setFullscreenFadeAnimation(
|
g_pDesktopAnimationManager->setFullscreenFadeAnimation(
|
||||||
PWORKSPACEB, PWORKSPACEB->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);
|
PWORKSPACEB, PWORKSPACEB->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);
|
||||||
g_pDesktopAnimationManager->setFullscreenFadeAnimation(
|
g_pDesktopAnimationManager->setFullscreenFadeAnimation(
|
||||||
|
|
@ -1982,7 +1953,6 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo
|
||||||
|
|
||||||
// move the workspace
|
// move the workspace
|
||||||
pWorkspace->m_monitor = pMonitor;
|
pWorkspace->m_monitor = pMonitor;
|
||||||
pWorkspace->m_space->recheckWorkArea();
|
|
||||||
pWorkspace->m_events.monitorChanged.emit();
|
pWorkspace->m_events.monitorChanged.emit();
|
||||||
|
|
||||||
for (auto const& w : m_windows) {
|
for (auto const& w : m_windows) {
|
||||||
|
|
@ -2038,7 +2008,6 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo
|
||||||
pWorkspace->m_events.activeChanged.emit();
|
pWorkspace->m_events.activeChanged.emit();
|
||||||
|
|
||||||
g_layoutManager->recalculateMonitor(pMonitor);
|
g_layoutManager->recalculateMonitor(pMonitor);
|
||||||
g_pHyprRenderer->damageMonitor(pMonitor);
|
|
||||||
|
|
||||||
g_pDesktopAnimationManager->startAnimation(pWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true);
|
g_pDesktopAnimationManager->startAnimation(pWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true);
|
||||||
pWorkspace->m_visible = true;
|
pWorkspace->m_visible = true;
|
||||||
|
|
@ -2778,7 +2747,6 @@ void CCompositor::arrangeMonitors() {
|
||||||
}
|
}
|
||||||
|
|
||||||
PROTO::xdgOutput->updateAllOutputs();
|
PROTO::xdgOutput->updateAllOutputs();
|
||||||
Event::bus()->m_events.monitor.layoutChanged.emit();
|
|
||||||
|
|
||||||
#ifndef NO_XWAYLAND
|
#ifndef NO_XWAYLAND
|
||||||
const auto box = g_pCompositor->calculateX11WorkArea();
|
const auto box = g_pCompositor->calculateX11WorkArea();
|
||||||
|
|
@ -3074,27 +3042,6 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCompositor::ensureWorkspacesOnAssignedMonitors() {
|
|
||||||
for (auto const& ws : getWorkspacesCopy()) {
|
|
||||||
if (!valid(ws) || ws->m_isSpecialWorkspace)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const auto RULE = g_pConfigManager->getWorkspaceRuleFor(ws);
|
|
||||||
if (RULE.monitor.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const auto PMONITOR = getMonitorFromString(RULE.monitor);
|
|
||||||
if (!PMONITOR)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (ws->m_monitor == PMONITOR)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Log::logger->log(Log::DEBUG, "ensureWorkspacesOnAssignedMonitors: moving workspace {} to {}", ws->m_name, PMONITOR->m_name);
|
|
||||||
moveWorkspaceToMonitor(ws, PMONITOR, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<unsigned int> CCompositor::getVTNr() {
|
std::optional<unsigned int> CCompositor::getVTNr() {
|
||||||
if (!m_aqBackend->hasSession())
|
if (!m_aqBackend->hasSession())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
#include "managers/KeybindManager.hpp"
|
#include "managers/KeybindManager.hpp"
|
||||||
#include "managers/SessionLockManager.hpp"
|
#include "managers/SessionLockManager.hpp"
|
||||||
#include "desktop/view/Window.hpp"
|
#include "desktop/view/Window.hpp"
|
||||||
#include "helpers/cm/ColorManagement.hpp"
|
#include "protocols/types/ColorManagement.hpp"
|
||||||
|
|
||||||
#include <aquamarine/backend/Backend.hpp>
|
#include <aquamarine/backend/Backend.hpp>
|
||||||
#include <aquamarine/output/Output.hpp>
|
#include <aquamarine/output/Output.hpp>
|
||||||
|
|
@ -161,7 +161,6 @@ class CCompositor {
|
||||||
void updateSuspendedStates();
|
void updateSuspendedStates();
|
||||||
void onNewMonitor(SP<Aquamarine::IOutput> output);
|
void onNewMonitor(SP<Aquamarine::IOutput> output);
|
||||||
void ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace = nullptr);
|
void ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace = nullptr);
|
||||||
void ensureWorkspacesOnAssignedMonitors();
|
|
||||||
std::optional<unsigned int> getVTNr();
|
std::optional<unsigned int> getVTNr();
|
||||||
bool isVRRActiveOnAnyMonitor() const;
|
bool isVRRActiveOnAnyMonitor() const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1579,18 +1579,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SBoolData{true},
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
},
|
},
|
||||||
SConfigOptionDescription{
|
|
||||||
.value = "render:icc_vcgt_enabled",
|
|
||||||
.description = "Enable sending VCGT ramps to KMS with ICC profiles",
|
|
||||||
.type = CONFIG_OPTION_BOOL,
|
|
||||||
.data = SConfigOptionDescription::SBoolData{true},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.value = "render:use_shader_blur_blend",
|
|
||||||
.description = "Use experimental blurred bg blending",
|
|
||||||
.type = CONFIG_OPTION_BOOL,
|
|
||||||
.data = SConfigOptionDescription::SBoolData{false},
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cursor:
|
* cursor:
|
||||||
|
|
@ -2105,18 +2093,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
.type = CONFIG_OPTION_CHOICE,
|
.type = CONFIG_OPTION_CHOICE,
|
||||||
.data = SConfigOptionDescription::SChoiceData{.firstIndex = 0, .choices = "right,left,down,up"},
|
.data = SConfigOptionDescription::SChoiceData{.firstIndex = 0, .choices = "right,left,down,up"},
|
||||||
},
|
},
|
||||||
SConfigOptionDescription{
|
|
||||||
.value = "scrolling:wrap_focus",
|
|
||||||
.description = "Determines if column focus wraps around when going before the first column or past the last column",
|
|
||||||
.type = CONFIG_OPTION_BOOL,
|
|
||||||
.data = SConfigOptionDescription::SBoolData{.value = true},
|
|
||||||
},
|
|
||||||
SConfigOptionDescription{
|
|
||||||
.value = "scrolling:wrap_swapcol",
|
|
||||||
.description = "Determines if column movement wraps around when moving to before the first column or past the last column",
|
|
||||||
.type = CONFIG_OPTION_BOOL,
|
|
||||||
.data = SConfigOptionDescription::SBoolData{.value = true},
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Quirks
|
* Quirks
|
||||||
|
|
|
||||||
|
|
@ -655,8 +655,6 @@ CConfigManager::CConfigManager() {
|
||||||
registerConfigVar("scrolling:follow_min_visible", Hyprlang::FLOAT{0.4});
|
registerConfigVar("scrolling:follow_min_visible", Hyprlang::FLOAT{0.4});
|
||||||
registerConfigVar("scrolling:explicit_column_widths", Hyprlang::STRING{"0.333, 0.5, 0.667, 1.0"});
|
registerConfigVar("scrolling:explicit_column_widths", Hyprlang::STRING{"0.333, 0.5, 0.667, 1.0"});
|
||||||
registerConfigVar("scrolling:direction", Hyprlang::STRING{"right"});
|
registerConfigVar("scrolling:direction", Hyprlang::STRING{"right"});
|
||||||
registerConfigVar("scrolling:wrap_focus", Hyprlang::INT{1});
|
|
||||||
registerConfigVar("scrolling:wrap_swapcol", Hyprlang::INT{1});
|
|
||||||
|
|
||||||
registerConfigVar("animations:enabled", Hyprlang::INT{1});
|
registerConfigVar("animations:enabled", Hyprlang::INT{1});
|
||||||
registerConfigVar("animations:workspace_wraparound", Hyprlang::INT{0});
|
registerConfigVar("animations:workspace_wraparound", Hyprlang::INT{0});
|
||||||
|
|
@ -799,8 +797,6 @@ CConfigManager::CConfigManager() {
|
||||||
registerConfigVar("render:non_shader_cm", Hyprlang::INT{3});
|
registerConfigVar("render:non_shader_cm", Hyprlang::INT{3});
|
||||||
registerConfigVar("render:cm_sdr_eotf", {"default"});
|
registerConfigVar("render:cm_sdr_eotf", {"default"});
|
||||||
registerConfigVar("render:commit_timing_enabled", Hyprlang::INT{1});
|
registerConfigVar("render:commit_timing_enabled", Hyprlang::INT{1});
|
||||||
registerConfigVar("render:icc_vcgt_enabled", Hyprlang::INT{1});
|
|
||||||
registerConfigVar("render:use_shader_blur_blend", Hyprlang::INT{0});
|
|
||||||
|
|
||||||
registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0});
|
registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0});
|
||||||
registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0});
|
registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0});
|
||||||
|
|
@ -875,7 +871,6 @@ CConfigManager::CConfigManager() {
|
||||||
m_config->addSpecialConfigValue("monitorv2", "min_luminance", Hyprlang::FLOAT{-1.0});
|
m_config->addSpecialConfigValue("monitorv2", "min_luminance", Hyprlang::FLOAT{-1.0});
|
||||||
m_config->addSpecialConfigValue("monitorv2", "max_luminance", Hyprlang::INT{-1});
|
m_config->addSpecialConfigValue("monitorv2", "max_luminance", Hyprlang::INT{-1});
|
||||||
m_config->addSpecialConfigValue("monitorv2", "max_avg_luminance", Hyprlang::INT{-1});
|
m_config->addSpecialConfigValue("monitorv2", "max_avg_luminance", Hyprlang::INT{-1});
|
||||||
m_config->addSpecialConfigValue("monitorv2", "icc", Hyprlang::STRING{""});
|
|
||||||
|
|
||||||
// windowrule v3
|
// windowrule v3
|
||||||
m_config->addSpecialCategory("windowrule", {.key = "name"});
|
m_config->addSpecialCategory("windowrule", {.key = "name"});
|
||||||
|
|
@ -1262,10 +1257,6 @@ std::optional<std::string> CConfigManager::handleMonitorv2(const std::string& ou
|
||||||
if (VAL && VAL->m_bSetByUser)
|
if (VAL && VAL->m_bSetByUser)
|
||||||
parser.rule().maxAvgLuminance = std::any_cast<Hyprlang::INT>(VAL->getValue());
|
parser.rule().maxAvgLuminance = std::any_cast<Hyprlang::INT>(VAL->getValue());
|
||||||
|
|
||||||
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "icc", output.c_str());
|
|
||||||
if (VAL && VAL->m_bSetByUser)
|
|
||||||
parser.rule().iccFile = std::any_cast<Hyprlang::STRING>(VAL->getValue());
|
|
||||||
|
|
||||||
auto newrule = parser.rule();
|
auto newrule = parser.rule();
|
||||||
|
|
||||||
std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == newrule.name; });
|
std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == newrule.name; });
|
||||||
|
|
@ -2284,15 +2275,6 @@ bool CMonitorRuleParser::parseVRR(const std::string& value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CMonitorRuleParser::parseICC(const std::string& val) {
|
|
||||||
if (val.empty()) {
|
|
||||||
m_error += "invalid icc ";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_rule.iccFile = val;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CMonitorRuleParser::setDisabled() {
|
void CMonitorRuleParser::setDisabled() {
|
||||||
m_rule.disabled = true;
|
m_rule.disabled = true;
|
||||||
}
|
}
|
||||||
|
|
@ -2387,9 +2369,6 @@ std::optional<std::string> CConfigManager::handleMonitor(const std::string& comm
|
||||||
} else if (ARGS[argno] == "vrr") {
|
} else if (ARGS[argno] == "vrr") {
|
||||||
parser.parseVRR(std::string(ARGS[argno + 1]));
|
parser.parseVRR(std::string(ARGS[argno + 1]));
|
||||||
argno++;
|
argno++;
|
||||||
} else if (ARGS[argno] == "icc") {
|
|
||||||
parser.parseICC(std::string(ARGS[argno + 1]));
|
|
||||||
argno++;
|
|
||||||
} else if (ARGS[argno] == "workspace") {
|
} else if (ARGS[argno] == "workspace") {
|
||||||
const auto& [id, name, isAutoID] = getWorkspaceIDNameFromString(std::string(ARGS[argno + 1]));
|
const auto& [id, name, isAutoID] = getWorkspaceIDNameFromString(std::string(ARGS[argno + 1]));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,6 @@ class CMonitorRuleParser {
|
||||||
bool parseSDRBrightness(const std::string& value);
|
bool parseSDRBrightness(const std::string& value);
|
||||||
bool parseSDRSaturation(const std::string& value);
|
bool parseSDRSaturation(const std::string& value);
|
||||||
bool parseVRR(const std::string& value);
|
bool parseVRR(const std::string& value);
|
||||||
bool parseICC(const std::string& value);
|
|
||||||
|
|
||||||
void setDisabled();
|
void setDisabled();
|
||||||
void setMirror(const std::string& value);
|
void setMirror(const std::string& value);
|
||||||
|
|
|
||||||
|
|
@ -2049,12 +2049,7 @@ static std::string submapRequest(eHyprCtlOutputFormat format, std::string reques
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) {
|
static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) {
|
||||||
CVarList vars(request, 0, ' ');
|
if (g_pHyprOpenGL->initShaders())
|
||||||
|
|
||||||
if (vars.size() > 2)
|
|
||||||
return "too many args";
|
|
||||||
|
|
||||||
if (g_pHyprOpenGL && g_pHyprRenderer->reloadShaders(vars.size() == 2 ? vars[1] : ""))
|
|
||||||
return format == FORMAT_JSON ? "{\"ok\": true}" : "ok";
|
return format == FORMAT_JSON ? "{\"ok\": true}" : "ok";
|
||||||
else
|
else
|
||||||
return format == FORMAT_JSON ? "{\"ok\": false}" : "error";
|
return format == FORMAT_JSON ? "{\"ok\": false}" : "error";
|
||||||
|
|
@ -2081,8 +2076,8 @@ CHyprCtl::CHyprCtl() {
|
||||||
registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
|
registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
|
||||||
registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions});
|
registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions});
|
||||||
registerCommand(SHyprCtlCommand{"submap", true, submapRequest});
|
registerCommand(SHyprCtlCommand{"submap", true, submapRequest});
|
||||||
|
registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = true, .fn = reloadShaders});
|
||||||
|
|
||||||
registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = false, .fn = reloadShaders});
|
|
||||||
registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
|
registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
|
||||||
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});
|
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});
|
||||||
registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});
|
registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});
|
||||||
|
|
@ -2202,15 +2197,6 @@ std::string CHyprCtl::getReply(std::string request) {
|
||||||
Desktop::Rule::ruleEngine()->updateAllRules();
|
Desktop::Rule::ruleEngine()->updateAllRules();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& ws : g_pCompositor->getWorkspaces()) {
|
|
||||||
if (!ws)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ws->updateWindows();
|
|
||||||
ws->updateWindowData();
|
|
||||||
ws->updateWindowDecos();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const& m : g_pCompositor->m_monitors) {
|
for (auto const& m : g_pCompositor->m_monitors) {
|
||||||
g_pHyprRenderer->damageMonitor(m);
|
g_pHyprRenderer->damageMonitor(m);
|
||||||
}
|
}
|
||||||
|
|
@ -2224,38 +2210,16 @@ std::string CHyprCtl::makeDynamicCall(const std::string& input) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool successWrite(int fd, const std::string& data, bool needLog = true) {
|
static bool successWrite(int fd, const std::string& data, bool needLog = true) {
|
||||||
size_t totalWritten = 0;
|
if (write(fd, data.c_str(), data.length()) > 0)
|
||||||
size_t remaining = data.length();
|
return true;
|
||||||
size_t waitsDone = 0;
|
|
||||||
constexpr const size_t MAX_WAITS = 20; // 2000µs = 2ms
|
|
||||||
|
|
||||||
while (totalWritten < data.length()) {
|
if (errno == EAGAIN)
|
||||||
ssize_t written = write(fd, data.c_str() + totalWritten, remaining);
|
return true;
|
||||||
|
|
||||||
if (waitsDone > MAX_WAITS) {
|
if (needLog)
|
||||||
Log::logger->log(Log::ERR, "Couldn't write to socket. Buffer was full and the client couldn't read in time.");
|
Log::logger->log(Log::ERR, "Couldn't write to socket. Error: " + std::string(strerror(errno)));
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (written < 0) {
|
return false;
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
||||||
// socket buffer full, wait a bit and retry
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(100));
|
|
||||||
waitsDone++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (needLog)
|
|
||||||
Log::logger->log(Log::ERR, "Couldn't write to socket. Error: {}", strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
waitsDone = 0;
|
|
||||||
|
|
||||||
totalWritten += written;
|
|
||||||
remaining -= written;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void runWritingDebugLogThread(const int conn) {
|
static void runWritingDebugLogThread(const int conn) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
#include "../desktop/state/FocusState.hpp"
|
#include "../desktop/state/FocusState.hpp"
|
||||||
|
|
||||||
CHyprDebugOverlay::CHyprDebugOverlay() {
|
CHyprDebugOverlay::CHyprDebugOverlay() {
|
||||||
m_texture = g_pHyprRenderer->createTexture();
|
m_texture = makeShared<CTexture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
|
void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
|
||||||
|
|
@ -259,7 +259,15 @@ void CHyprDebugOverlay::draw() {
|
||||||
cairo_surface_flush(m_cairoSurface);
|
cairo_surface_flush(m_cairoSurface);
|
||||||
|
|
||||||
// copy the data to an OpenGL texture we have
|
// copy the data to an OpenGL texture we have
|
||||||
m_texture = g_pHyprRenderer->createTexture(m_cairoSurface);
|
const auto DATA = cairo_image_surface_get_data(m_cairoSurface);
|
||||||
|
m_texture->allocate();
|
||||||
|
m_texture->bind();
|
||||||
|
m_texture->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
m_texture->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||||
|
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||||
|
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||||
|
|
||||||
CTexPassElement::SRenderData data;
|
CTexPassElement::SRenderData data;
|
||||||
data.tex = m_texture;
|
data.tex = m_texture;
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ class CHyprDebugOverlay {
|
||||||
cairo_surface_t* m_cairoSurface = nullptr;
|
cairo_surface_t* m_cairoSurface = nullptr;
|
||||||
cairo_t* m_cairo = nullptr;
|
cairo_t* m_cairo = nullptr;
|
||||||
|
|
||||||
SP<ITexture> m_texture;
|
SP<CTexture> m_texture;
|
||||||
|
|
||||||
friend class CHyprMonitorDebugOverlay;
|
friend class CHyprMonitorDebugOverlay;
|
||||||
friend class CHyprRenderer;
|
friend class CHyprRenderer;
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ CHyprNotificationOverlay::CHyprNotificationOverlay() {
|
||||||
|
|
||||||
g_pHyprRenderer->damageBox(m_lastDamage);
|
g_pHyprRenderer->damageBox(m_lastDamage);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_texture = makeShared<CTexture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
CHyprNotificationOverlay::~CHyprNotificationOverlay() {
|
CHyprNotificationOverlay::~CHyprNotificationOverlay() {
|
||||||
|
|
@ -230,7 +232,16 @@ void CHyprNotificationOverlay::draw(PHLMONITOR pMonitor) {
|
||||||
|
|
||||||
m_lastDamage = damage;
|
m_lastDamage = damage;
|
||||||
|
|
||||||
m_texture = g_pHyprRenderer->createTexture(m_cairoSurface);
|
// copy the data to an OpenGL texture we have
|
||||||
|
const auto DATA = cairo_image_surface_get_data(m_cairoSurface);
|
||||||
|
m_texture->allocate();
|
||||||
|
m_texture->bind();
|
||||||
|
m_texture->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
m_texture->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||||
|
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||||
|
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MONSIZE.x, MONSIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||||
|
|
||||||
CTexPassElement::SRenderData data;
|
CTexPassElement::SRenderData data;
|
||||||
data.tex = m_texture;
|
data.tex = m_texture;
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ enum eIconBackend : uint8_t {
|
||||||
static const std::array<std::array<std::string, ICON_NONE + 1>, 3 /* backends */> ICONS_ARRAY = {
|
static const std::array<std::array<std::string, ICON_NONE + 1>, 3 /* backends */> ICONS_ARRAY = {
|
||||||
std::array<std::string, ICON_NONE + 1>{"[!]", "[i]", "[Hint]", "[Err]", "[?]", "[ok]", ""},
|
std::array<std::string, ICON_NONE + 1>{"[!]", "[i]", "[Hint]", "[Err]", "[?]", "[ok]", ""},
|
||||||
std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", "", ""}, std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", ""}};
|
std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", "", ""}, std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", ""}};
|
||||||
static const std::array<CHyprColor, ICON_NONE + 1> ICONS_COLORS = {CHyprColor{1.0, 204 / 255.0, 102 / 255.0, 1.0},
|
static const std::array<CHyprColor, ICON_NONE + 1> ICONS_COLORS = {CHyprColor{255.0 / 255.0, 204 / 255.0, 102 / 255.0, 1.0},
|
||||||
CHyprColor{128 / 255.0, 255 / 255.0, 255 / 255.0, 1.0},
|
CHyprColor{128 / 255.0, 255 / 255.0, 255 / 255.0, 1.0},
|
||||||
CHyprColor{179 / 255.0, 255 / 255.0, 204 / 255.0, 1.0},
|
CHyprColor{179 / 255.0, 255 / 255.0, 204 / 255.0, 1.0},
|
||||||
CHyprColor{255 / 255.0, 77 / 255.0, 77 / 255.0, 1.0},
|
CHyprColor{255 / 255.0, 77 / 255.0, 77 / 255.0, 1.0},
|
||||||
|
|
@ -57,7 +57,7 @@ class CHyprNotificationOverlay {
|
||||||
PHLMONITORREF m_lastMonitor;
|
PHLMONITORREF m_lastMonitor;
|
||||||
Vector2D m_lastSize = Vector2D(-1, -1);
|
Vector2D m_lastSize = Vector2D(-1, -1);
|
||||||
|
|
||||||
SP<ITexture> m_texture;
|
SP<CTexture> m_texture;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline UP<CHyprNotificationOverlay> g_pHyprNotificationOverlay;
|
inline UP<CHyprNotificationOverlay> g_pHyprNotificationOverlay;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
#include "../../view/LayerSurface.hpp"
|
#include "../../view/LayerSurface.hpp"
|
||||||
#include "../../types/OverridableVar.hpp"
|
#include "../../types/OverridableVar.hpp"
|
||||||
#include "../../../helpers/MiscFunctions.hpp"
|
#include "../../../helpers/MiscFunctions.hpp"
|
||||||
#include "../../../event/EventBus.hpp"
|
|
||||||
|
|
||||||
using namespace Desktop;
|
using namespace Desktop;
|
||||||
using namespace Desktop::Rule;
|
using namespace Desktop::Rule;
|
||||||
|
|
@ -33,38 +32,11 @@ void CLayerRuleApplicator::resetProps(std::underlying_type_t<eRuleProperty> prop
|
||||||
UNSET(aboveLock)
|
UNSET(aboveLock)
|
||||||
UNSET(ignoreAlpha)
|
UNSET(ignoreAlpha)
|
||||||
UNSET(animationStyle)
|
UNSET(animationStyle)
|
||||||
|
|
||||||
#undef UNSET
|
|
||||||
|
|
||||||
if (prio == Types::PRIORITY_WINDOW_RULE)
|
|
||||||
std::erase_if(m_otherProps.props, [props](const auto& el) { return !el.second || el.second->propMask & props; });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLayerRuleApplicator::applyDynamicRule(const SP<CLayerRule>& rule) {
|
void CLayerRuleApplicator::applyDynamicRule(const SP<CLayerRule>& rule) {
|
||||||
for (const auto& [key, effect] : rule->effects()) {
|
for (const auto& [key, effect] : rule->effects()) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
default: {
|
|
||||||
if (key <= LAYER_RULE_EFFECT_LAST_STATIC) {
|
|
||||||
Log::logger->log(Log::TRACE, "CLayerRuleApplicator::applyDynamicRule: Skipping effect {}, not dynamic", sc<std::underlying_type_t<eLayerRuleEffect>>(key));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom type, add to our vec
|
|
||||||
if (!m_otherProps.props.contains(key)) {
|
|
||||||
m_otherProps.props.emplace(key,
|
|
||||||
makeUnique<SCustomPropContainer>(SCustomPropContainer{
|
|
||||||
.idx = key,
|
|
||||||
.propMask = rule->getPropertiesMask(),
|
|
||||||
.effect = effect,
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
auto& e = m_otherProps.props[key];
|
|
||||||
e->propMask |= rule->getPropertiesMask();
|
|
||||||
e->effect = effect;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LAYER_RULE_EFFECT_NONE: {
|
case LAYER_RULE_EFFECT_NONE: {
|
||||||
Log::logger->log(Log::ERR, "CLayerRuleApplicator::applyDynamicRule: BUG THIS: LAYER_RULE_EFFECT_NONE??");
|
Log::logger->log(Log::ERR, "CLayerRuleApplicator::applyDynamicRule: BUG THIS: LAYER_RULE_EFFECT_NONE??");
|
||||||
break;
|
break;
|
||||||
|
|
@ -153,7 +125,4 @@ void CLayerRuleApplicator::propertiesChanged(std::underlying_type_t<eRulePropert
|
||||||
|
|
||||||
applyDynamicRule(wr);
|
applyDynamicRule(wr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// for plugins
|
|
||||||
Event::bus()->m_events.layer.updateRules.emit(m_ls.lock());
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "LayerRuleEffectContainer.hpp"
|
|
||||||
#include "../../DesktopTypes.hpp"
|
#include "../../DesktopTypes.hpp"
|
||||||
#include "../Rule.hpp"
|
#include "../Rule.hpp"
|
||||||
#include "../../types/OverridableVar.hpp"
|
#include "../../types/OverridableVar.hpp"
|
||||||
|
|
@ -22,17 +21,6 @@ namespace Desktop::Rule {
|
||||||
void propertiesChanged(std::underlying_type_t<eRuleProperty> props);
|
void propertiesChanged(std::underlying_type_t<eRuleProperty> props);
|
||||||
void resetProps(std::underlying_type_t<eRuleProperty> props, Types::eOverridePriority prio = Types::PRIORITY_WINDOW_RULE);
|
void resetProps(std::underlying_type_t<eRuleProperty> props, Types::eOverridePriority prio = Types::PRIORITY_WINDOW_RULE);
|
||||||
|
|
||||||
struct SCustomPropContainer {
|
|
||||||
CLayerRuleEffectContainer::storageType idx = LAYER_RULE_EFFECT_NONE;
|
|
||||||
std::underlying_type_t<eRuleProperty> propMask = RULE_PROP_NONE;
|
|
||||||
std::string effect;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This struct holds props that were dynamically registered. Plugins may read this.
|
|
||||||
struct {
|
|
||||||
std::unordered_map<CLayerRuleEffectContainer::storageType, UP<SCustomPropContainer>> props;
|
|
||||||
} m_otherProps;
|
|
||||||
|
|
||||||
#define COMMA ,
|
#define COMMA ,
|
||||||
#define DEFINE_PROP(type, name, def) \
|
#define DEFINE_PROP(type, name, def) \
|
||||||
private: \
|
private: \
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ bool CWindowRule::matches(PHLWINDOW w, bool allowEnvLookup) {
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case RULE_PROP_CONTENT:
|
case RULE_PROP_CONTENT:
|
||||||
if (!engine->match(w->getContentType()) && !engine->match(NContentType::toString(w->getContentType())))
|
if (!engine->match(w->getContentType()))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case RULE_PROP_XDG_TAG:
|
case RULE_PROP_XDG_TAG:
|
||||||
|
|
|
||||||
|
|
@ -630,8 +630,6 @@ void CWindowRuleApplicator::propertiesChanged(std::underlying_type_t<eRuleProper
|
||||||
needsRelayout = needsRelayout || RES.needsRelayout;
|
needsRelayout = needsRelayout || RES.needsRelayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_window->updateWindowData();
|
|
||||||
m_window->updateWindowDecos();
|
|
||||||
m_window->updateDecorationValues();
|
m_window->updateDecorationValues();
|
||||||
|
|
||||||
if (needsRelayout)
|
if (needsRelayout)
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ void CGroup::add(PHLWINDOW w) {
|
||||||
m_target->recalc();
|
m_target->recalc();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGroup::remove(PHLWINDOW w, Math::eDirection dir) {
|
void CGroup::remove(PHLWINDOW w) {
|
||||||
std::optional<size_t> idx;
|
std::optional<size_t> idx;
|
||||||
for (size_t i = 0; i < m_windows.size(); ++i) {
|
for (size_t i = 0; i < m_windows.size(); ++i) {
|
||||||
if (m_windows.at(i) == w) {
|
if (m_windows.at(i) == w) {
|
||||||
|
|
@ -156,20 +156,8 @@ void CGroup::remove(PHLWINDOW w, Math::eDirection dir) {
|
||||||
updateWindowVisibility();
|
updateWindowVisibility();
|
||||||
|
|
||||||
// do this here: otherwise the new current is hidden and workspace rules get wrong data
|
// do this here: otherwise the new current is hidden and workspace rules get wrong data
|
||||||
if (!REMOVING_GROUP) {
|
if (!REMOVING_GROUP)
|
||||||
std::optional<Vector2D> focalPoint;
|
w->m_target->assignToSpace(m_target->space());
|
||||||
if (dir != Math::DIRECTION_DEFAULT) {
|
|
||||||
const auto box = m_target->position();
|
|
||||||
switch (dir) {
|
|
||||||
case Math::DIRECTION_RIGHT: focalPoint = Vector2D(box.x + box.w, box.y + box.h / 2.0); break;
|
|
||||||
case Math::DIRECTION_LEFT: focalPoint = Vector2D(box.x, box.y + box.h / 2.0); break;
|
|
||||||
case Math::DIRECTION_DOWN: focalPoint = Vector2D(box.x + box.w / 2.0, box.y + box.h); break;
|
|
||||||
case Math::DIRECTION_UP: focalPoint = Vector2D(box.x + box.w / 2.0, box.y); break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w->m_target->assignToSpace(m_target->space(), focalPoint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGroup::moveCurrent(bool next) {
|
void CGroup::moveCurrent(bool next) {
|
||||||
|
|
@ -329,7 +317,6 @@ void CGroup::swapWithNext() {
|
||||||
|
|
||||||
size_t idx = m_current + 1 >= m_windows.size() ? 0 : m_current + 1;
|
size_t idx = m_current + 1 >= m_windows.size() ? 0 : m_current + 1;
|
||||||
std::iter_swap(m_windows.begin() + m_current, m_windows.begin() + idx);
|
std::iter_swap(m_windows.begin() + m_current, m_windows.begin() + idx);
|
||||||
m_current = idx;
|
|
||||||
|
|
||||||
updateWindowVisibility();
|
updateWindowVisibility();
|
||||||
|
|
||||||
|
|
@ -342,7 +329,6 @@ void CGroup::swapWithLast() {
|
||||||
|
|
||||||
size_t idx = m_current == 0 ? m_windows.size() - 1 : m_current - 1;
|
size_t idx = m_current == 0 ? m_windows.size() - 1 : m_current - 1;
|
||||||
std::iter_swap(m_windows.begin() + m_current, m_windows.begin() + idx);
|
std::iter_swap(m_windows.begin() + m_current, m_windows.begin() + idx);
|
||||||
m_current = idx;
|
|
||||||
|
|
||||||
updateWindowVisibility();
|
updateWindowVisibility();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../DesktopTypes.hpp"
|
#include "../DesktopTypes.hpp"
|
||||||
#include "../../helpers/math/Direction.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
@ -18,7 +17,7 @@ namespace Desktop::View {
|
||||||
bool has(PHLWINDOW w) const;
|
bool has(PHLWINDOW w) const;
|
||||||
|
|
||||||
void add(PHLWINDOW w);
|
void add(PHLWINDOW w);
|
||||||
void remove(PHLWINDOW w, Math::eDirection dir = Math::DIRECTION_DEFAULT);
|
void remove(PHLWINDOW w);
|
||||||
void moveCurrent(bool next);
|
void moveCurrent(bool next);
|
||||||
void setCurrent(size_t idx);
|
void setCurrent(size_t idx);
|
||||||
void setCurrent(PHLWINDOW w);
|
void setCurrent(PHLWINDOW w);
|
||||||
|
|
|
||||||
|
|
@ -277,7 +277,7 @@ CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
|
||||||
|
|
||||||
// fucker fucking fuck
|
// fucker fucking fuck
|
||||||
const auto WORKAREA = m_workspace->m_space->workArea();
|
const auto WORKAREA = m_workspace->m_space->workArea();
|
||||||
const auto& RESERVED = CReservedArea(PMONITOR->logicalBox(), WORKAREA);
|
const auto& RESERVED = PMONITOR->m_reservedArea;
|
||||||
|
|
||||||
if (DELTALESSTHAN(POS.x, WORKAREA.x, 1)) {
|
if (DELTALESSTHAN(POS.x, WORKAREA.x, 1)) {
|
||||||
POS.x -= RESERVED.left();
|
POS.x -= RESERVED.left();
|
||||||
|
|
@ -510,6 +510,12 @@ void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) {
|
||||||
|
|
||||||
setAnimationsToMove();
|
setAnimationsToMove();
|
||||||
|
|
||||||
|
OLDWORKSPACE->updateWindows();
|
||||||
|
OLDWORKSPACE->updateWindowData();
|
||||||
|
|
||||||
|
pWorkspace->updateWindows();
|
||||||
|
pWorkspace->updateWindowData();
|
||||||
|
|
||||||
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
||||||
|
|
||||||
if (valid(pWorkspace)) {
|
if (valid(pWorkspace)) {
|
||||||
|
|
@ -801,13 +807,9 @@ void CWindow::updateWindowData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWindow::updateWindowData(const SWorkspaceRule& workspaceRule) {
|
void CWindow::updateWindowData(const SWorkspaceRule& workspaceRule) {
|
||||||
if (workspaceRule.noBorder.value_or(false))
|
m_ruleApplicator->borderSize().matchOptional(workspaceRule.borderSize, Desktop::Types::PRIORITY_WORKSPACE_RULE);
|
||||||
m_ruleApplicator->borderSize().matchOptional(std::optional<Hyprlang::INT>(0), Desktop::Types::PRIORITY_WORKSPACE_RULE);
|
|
||||||
else if (workspaceRule.borderSize)
|
|
||||||
m_ruleApplicator->borderSize().matchOptional(workspaceRule.borderSize, Desktop::Types::PRIORITY_WORKSPACE_RULE);
|
|
||||||
else
|
|
||||||
m_ruleApplicator->borderSize().matchOptional(std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE);
|
|
||||||
m_ruleApplicator->decorate().matchOptional(workspaceRule.decorate, Desktop::Types::PRIORITY_WORKSPACE_RULE);
|
m_ruleApplicator->decorate().matchOptional(workspaceRule.decorate, Desktop::Types::PRIORITY_WORKSPACE_RULE);
|
||||||
|
m_ruleApplicator->borderSize().matchOptional(workspaceRule.noBorder ? std::optional<Hyprlang::INT>(0) : std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE);
|
||||||
m_ruleApplicator->rounding().matchOptional(workspaceRule.noRounding.value_or(false) ? std::optional<Hyprlang::INT>(0) : std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE);
|
m_ruleApplicator->rounding().matchOptional(workspaceRule.noRounding.value_or(false) ? std::optional<Hyprlang::INT>(0) : std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE);
|
||||||
m_ruleApplicator->noShadow().matchOptional(workspaceRule.noShadow, Desktop::Types::PRIORITY_WORKSPACE_RULE);
|
m_ruleApplicator->noShadow().matchOptional(workspaceRule.noShadow, Desktop::Types::PRIORITY_WORKSPACE_RULE);
|
||||||
}
|
}
|
||||||
|
|
@ -1871,12 +1873,11 @@ void CWindow::mapWindow() {
|
||||||
if (WORKSPACEARGS.contains("silent"))
|
if (WORKSPACEARGS.contains("silent"))
|
||||||
workspaceSilent = true;
|
workspaceSilent = true;
|
||||||
|
|
||||||
auto joined = WORKSPACEARGS.join(" ", 0, workspaceSilent ? WORKSPACEARGS.size() - 1 : 0);
|
if (WORKSPACEARGS.contains("empty") && PWORKSPACE->getWindows() <= 1) {
|
||||||
if (joined.starts_with("empty") && PWORKSPACE->getWindows() == 0) {
|
|
||||||
requestedWorkspaceID = PWORKSPACE->m_id;
|
requestedWorkspaceID = PWORKSPACE->m_id;
|
||||||
requestedWorkspaceName = PWORKSPACE->m_name;
|
requestedWorkspaceName = PWORKSPACE->m_name;
|
||||||
} else {
|
} else {
|
||||||
auto result = getWorkspaceIDNameFromString(joined);
|
auto result = getWorkspaceIDNameFromString(WORKSPACEARGS.join(" ", 0, workspaceSilent ? WORKSPACEARGS.size() - 1 : 0));
|
||||||
requestedWorkspaceID = result.id;
|
requestedWorkspaceID = result.id;
|
||||||
requestedWorkspaceName = result.name;
|
requestedWorkspaceName = result.name;
|
||||||
}
|
}
|
||||||
|
|
@ -1948,13 +1949,11 @@ void CWindow::mapWindow() {
|
||||||
g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", m_self.lock(), PWORKSPACE->m_name, m_class, m_title)});
|
g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", m_self.lock(), PWORKSPACE->m_name, m_class, m_title)});
|
||||||
Event::bus()->m_events.window.openEarly.emit(m_self.lock());
|
Event::bus()->m_events.window.openEarly.emit(m_self.lock());
|
||||||
|
|
||||||
if (*PAUTOGROUP // auto_group enabled
|
if (*PAUTOGROUP // auto_group enabled
|
||||||
&& Desktop::focusState()->window() // focused window exists
|
&& Desktop::focusState()->window() // focused window exists
|
||||||
&& canBeGroupedInto(Desktop::focusState()->window()->m_group) // we can group
|
&& canBeGroupedInto(Desktop::focusState()->window()->m_group) // we can group
|
||||||
&& Desktop::focusState()->window()->m_workspace == m_workspace // workspaces match, we're not opening on another ws
|
&& Desktop::focusState()->window()->m_workspace == m_workspace // workspaces match, we're not opening on another ws
|
||||||
&& !g_pXWaylandManager->shouldBeFloated(m_self.lock()) && !isX11OverrideRedirect() // not a window that should float or X11
|
&& !isModal() && !(parent() && m_isFloating) && !isX11OverrideRedirect() // not a modal, floating child or X11 OR
|
||||||
&& !(m_isFloating && !Desktop::focusState()->window()->m_isFloating) // do not auto-group a floated window into a tiled group
|
|
||||||
&& !isModal() // no modal grouping
|
|
||||||
) {
|
) {
|
||||||
// add to group if we are focused on one
|
// add to group if we are focused on one
|
||||||
Desktop::focusState()->window()->m_group->add(m_self.lock());
|
Desktop::focusState()->window()->m_group->add(m_self.lock());
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@ namespace Event {
|
||||||
Event<PHLWINDOW> openEarly;
|
Event<PHLWINDOW> openEarly;
|
||||||
Event<PHLWINDOW> destroy;
|
Event<PHLWINDOW> destroy;
|
||||||
Event<PHLWINDOW> close;
|
Event<PHLWINDOW> close;
|
||||||
Event<PHLWINDOW> kill;
|
|
||||||
Event<PHLWINDOW, Desktop::eFocusReason> active;
|
Event<PHLWINDOW, Desktop::eFocusReason> active;
|
||||||
Event<PHLWINDOW> urgent;
|
Event<PHLWINDOW> urgent;
|
||||||
Event<PHLWINDOW> title;
|
Event<PHLWINDOW> title;
|
||||||
|
|
@ -55,7 +54,6 @@ namespace Event {
|
||||||
struct {
|
struct {
|
||||||
Event<PHLLS> opened;
|
Event<PHLLS> opened;
|
||||||
Event<PHLLS> closed;
|
Event<PHLLS> closed;
|
||||||
Event<PHLLS> updateRules;
|
|
||||||
} layer;
|
} layer;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
@ -141,4 +139,4 @@ namespace Event {
|
||||||
};
|
};
|
||||||
|
|
||||||
UP<CEventBus>& bus();
|
UP<CEventBus>& bus();
|
||||||
};
|
};
|
||||||
|
|
@ -297,23 +297,31 @@ int NFormatUtils::minStride(const SPixelFormat* const fmt, int32_t width) {
|
||||||
return std::ceil((width * fmt->bytesPerBlock) / pixelsPerBlock(fmt));
|
return std::ceil((width * fmt->bytesPerBlock) / pixelsPerBlock(fmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t NFormatUtils::drmFormatToGL(DRMFormat drm) {
|
||||||
|
switch (drm) {
|
||||||
|
case DRM_FORMAT_XRGB8888:
|
||||||
|
case DRM_FORMAT_XBGR8888: return GL_RGBA; // doesn't matter, opengl is gucci in this case.
|
||||||
|
case DRM_FORMAT_XRGB2101010:
|
||||||
|
case DRM_FORMAT_XBGR2101010: return GL_RGB10_A2;
|
||||||
|
default: return GL_RGBA;
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return GL_RGBA;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t NFormatUtils::glFormatToType(uint32_t gl) {
|
||||||
|
return gl != GL_RGBA ? GL_UNSIGNED_INT_2_10_10_10_REV : GL_UNSIGNED_BYTE;
|
||||||
|
}
|
||||||
|
|
||||||
std::string NFormatUtils::drmFormatName(DRMFormat drm) {
|
std::string NFormatUtils::drmFormatName(DRMFormat drm) {
|
||||||
auto n = drmGetFormatName(drm);
|
auto n = drmGetFormatName(drm);
|
||||||
|
|
||||||
if (!n)
|
|
||||||
return "unknown";
|
|
||||||
|
|
||||||
std::string name = n;
|
std::string name = n;
|
||||||
free(n); // NOLINT(cppcoreguidelines-no-malloc,-warnings-as-errors)
|
free(n); // NOLINT(cppcoreguidelines-no-malloc,-warnings-as-errors)
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string NFormatUtils::drmModifierName(uint64_t mod) {
|
std::string NFormatUtils::drmModifierName(uint64_t mod) {
|
||||||
auto n = drmGetFormatModifierName(mod);
|
auto n = drmGetFormatModifierName(mod);
|
||||||
|
|
||||||
if (!n)
|
|
||||||
return "unknown";
|
|
||||||
|
|
||||||
std::string name = n;
|
std::string name = n;
|
||||||
free(n); // NOLINT(cppcoreguidelines-no-malloc,-warnings-as-errors)
|
free(n); // NOLINT(cppcoreguidelines-no-malloc,-warnings-as-errors)
|
||||||
return name;
|
return name;
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ namespace NFormatUtils {
|
||||||
bool isFormatOpaque(DRMFormat drm);
|
bool isFormatOpaque(DRMFormat drm);
|
||||||
int pixelsPerBlock(const SPixelFormat* const fmt);
|
int pixelsPerBlock(const SPixelFormat* const fmt);
|
||||||
int minStride(const SPixelFormat* const fmt, int32_t width);
|
int minStride(const SPixelFormat* const fmt, int32_t width);
|
||||||
|
uint32_t drmFormatToGL(DRMFormat drm);
|
||||||
|
uint32_t glFormatToType(uint32_t gl);
|
||||||
std::string drmFormatName(DRMFormat drm);
|
std::string drmFormatName(DRMFormat drm);
|
||||||
std::string drmModifierName(uint64_t mod);
|
std::string drmModifierName(uint64_t mod);
|
||||||
DRMFormat alphaFormat(DRMFormat prevFormat);
|
DRMFormat alphaFormat(DRMFormat prevFormat);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
#include "../hyprerror/HyprError.hpp"
|
#include "../hyprerror/HyprError.hpp"
|
||||||
#include "../layout/LayoutManager.hpp"
|
#include "../layout/LayoutManager.hpp"
|
||||||
#include "../i18n/Engine.hpp"
|
#include "../i18n/Engine.hpp"
|
||||||
#include "../helpers/cm/ColorManagement.hpp"
|
#include "../protocols/types/ColorManagement.hpp"
|
||||||
#include "sync/SyncTimeline.hpp"
|
#include "sync/SyncTimeline.hpp"
|
||||||
#include "time/Time.hpp"
|
#include "time/Time.hpp"
|
||||||
#include "../desktop/view/LayerSurface.hpp"
|
#include "../desktop/view/LayerSurface.hpp"
|
||||||
|
|
@ -82,10 +82,7 @@ void CMonitor::onConnect(bool noRule) {
|
||||||
m_zoomAnimProgress->setValueAndWarp(0.F);
|
m_zoomAnimProgress->setValueAndWarp(0.F);
|
||||||
m_zoomAnimFrameCounter = 0;
|
m_zoomAnimFrameCounter = 0;
|
||||||
|
|
||||||
g_pEventLoopManager->doLater([] {
|
g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); });
|
||||||
g_pConfigManager->ensurePersistentWorkspacesPresent();
|
|
||||||
g_pCompositor->ensureWorkspacesOnAssignedMonitors();
|
|
||||||
});
|
|
||||||
|
|
||||||
m_listeners.frame = m_output->events.frame.listen([this] {
|
m_listeners.frame = m_output->events.frame.listen([this] {
|
||||||
if (m_frameScheduler)
|
if (m_frameScheduler)
|
||||||
|
|
@ -293,16 +290,10 @@ void CMonitor::onConnect(bool noRule) {
|
||||||
if (!valid(ws))
|
if (!valid(ws))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto CURRENTMON = ws->m_monitor.lock();
|
if (ws->m_lastMonitor == m_name || g_pCompositor->m_monitors.size() == 1 /* avoid lost workspaces on recover */) {
|
||||||
const bool ORPHANED = !CURRENTMON || std::ranges::none_of(g_pCompositor->m_monitors, [&](const auto& mon) { return mon == CURRENTMON; });
|
|
||||||
const bool RETURNING = ws->m_lastMonitor == m_name;
|
|
||||||
const bool RECOVERY = g_pCompositor->m_monitors.size() == 1 && ORPHANED; // temporarily recover orphaned workspaces
|
|
||||||
|
|
||||||
if (RETURNING || RECOVERY) {
|
|
||||||
g_pCompositor->moveWorkspaceToMonitor(ws, m_self.lock());
|
g_pCompositor->moveWorkspaceToMonitor(ws, m_self.lock());
|
||||||
g_pDesktopAnimationManager->startAnimation(ws, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true);
|
g_pDesktopAnimationManager->startAnimation(ws, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true);
|
||||||
if (RETURNING)
|
ws->m_lastMonitor = "";
|
||||||
ws->m_lastMonitor = "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,24 +429,19 @@ void CMonitor::onDisconnect(bool destroy) {
|
||||||
m_enabled = false;
|
m_enabled = false;
|
||||||
m_renderingInitPassed = false;
|
m_renderingInitPassed = false;
|
||||||
|
|
||||||
std::vector<PHLWORKSPACE> wspToMove;
|
|
||||||
for (auto const& w : g_pCompositor->getWorkspaces()) {
|
|
||||||
if (w->m_monitor == m_self || !w->m_monitor)
|
|
||||||
wspToMove.emplace_back(w.lock());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Preserve ownership across cascaded monitor disconnects.
|
|
||||||
// The first disconnected monitor "owns" where a workspace should return.
|
|
||||||
for (auto const& w : wspToMove) {
|
|
||||||
if (w && w->m_lastMonitor.empty())
|
|
||||||
w->m_lastMonitor = m_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BACKUPMON) {
|
if (BACKUPMON) {
|
||||||
// snap cursor
|
// snap cursor
|
||||||
g_pCompositor->warpCursorTo(BACKUPMON->m_position + BACKUPMON->m_transformedSize / 2.F, true);
|
g_pCompositor->warpCursorTo(BACKUPMON->m_position + BACKUPMON->m_transformedSize / 2.F, true);
|
||||||
|
|
||||||
|
// move workspaces
|
||||||
|
std::vector<PHLWORKSPACE> wspToMove;
|
||||||
|
for (auto const& w : g_pCompositor->getWorkspaces()) {
|
||||||
|
if (w->m_monitor == m_self || !w->m_monitor)
|
||||||
|
wspToMove.emplace_back(w.lock());
|
||||||
|
}
|
||||||
|
|
||||||
for (auto const& w : wspToMove) {
|
for (auto const& w : wspToMove) {
|
||||||
|
w->m_lastMonitor = m_name;
|
||||||
g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON);
|
g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON);
|
||||||
g_pDesktopAnimationManager->startAnimation(w, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true);
|
g_pDesktopAnimationManager->startAnimation(w, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true);
|
||||||
}
|
}
|
||||||
|
|
@ -933,46 +919,29 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
|
||||||
m_supportsWideColor = RULE->supportsHDR;
|
m_supportsWideColor = RULE->supportsHDR;
|
||||||
m_supportsHDR = RULE->supportsHDR;
|
m_supportsHDR = RULE->supportsHDR;
|
||||||
|
|
||||||
if (RULE->iccFile.empty()) {
|
m_cmType = RULE->cmType;
|
||||||
// only apply explicit cm settings if we have no icc file
|
switch (m_cmType) {
|
||||||
|
case NCMType::CM_AUTO: m_cmType = m_enabled10bit && supportsWideColor() ? NCMType::CM_WIDE : NCMType::CM_SRGB; break;
|
||||||
m_cmType = RULE->cmType;
|
case NCMType::CM_EDID: m_cmType = m_output->parsedEDID.chromaticityCoords.has_value() ? NCMType::CM_EDID : NCMType::CM_SRGB; break;
|
||||||
switch (m_cmType) {
|
case NCMType::CM_HDR:
|
||||||
case NCMType::CM_AUTO: m_cmType = m_enabled10bit && supportsWideColor() ? NCMType::CM_WIDE : NCMType::CM_SRGB; break;
|
case NCMType::CM_HDR_EDID: m_cmType = supportsHDR() ? m_cmType : NCMType::CM_SRGB; break;
|
||||||
case NCMType::CM_EDID: m_cmType = m_output->parsedEDID.chromaticityCoords.has_value() ? NCMType::CM_EDID : NCMType::CM_SRGB; break;
|
default: break;
|
||||||
case NCMType::CM_HDR:
|
|
||||||
case NCMType::CM_HDR_EDID: m_cmType = supportsHDR() ? m_cmType : NCMType::CM_SRGB; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_sdrEotf = RULE->sdrEotf;
|
|
||||||
|
|
||||||
m_sdrMinLuminance = RULE->sdrMinLuminance;
|
|
||||||
m_sdrMaxLuminance = RULE->sdrMaxLuminance;
|
|
||||||
|
|
||||||
m_minLuminance = RULE->minLuminance;
|
|
||||||
m_maxLuminance = RULE->maxLuminance;
|
|
||||||
m_maxAvgLuminance = RULE->maxAvgLuminance;
|
|
||||||
|
|
||||||
applyCMType(m_cmType, m_sdrEotf);
|
|
||||||
|
|
||||||
m_sdrSaturation = RULE->sdrSaturation;
|
|
||||||
m_sdrBrightness = RULE->sdrBrightness;
|
|
||||||
} else {
|
|
||||||
auto image = NColorManagement::SImageDescription::fromICC(RULE->iccFile);
|
|
||||||
if (!image) {
|
|
||||||
Log::logger->log(Log::ERR, "icc for {} ({}) failed: {}", m_name, RULE->iccFile, image.error());
|
|
||||||
g_pConfigManager->addParseError(std::format("failed to apply icc {} to {}: {}", RULE->iccFile, m_name, image.error()));
|
|
||||||
} else {
|
|
||||||
m_imageDescription = CImageDescription::from(*image);
|
|
||||||
if (!m_imageDescription) {
|
|
||||||
Log::logger->log(Log::ERR, "icc for {} ({}) failed 2: {}", m_name, RULE->iccFile, image.error());
|
|
||||||
g_pConfigManager->addParseError(std::format("failed to apply icc {} to {}: {}", RULE->iccFile, m_name, image.error()));
|
|
||||||
m_imageDescription = CImageDescription::from(SImageDescription{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_sdrEotf = RULE->sdrEotf;
|
||||||
|
|
||||||
|
m_sdrMinLuminance = RULE->sdrMinLuminance;
|
||||||
|
m_sdrMaxLuminance = RULE->sdrMaxLuminance;
|
||||||
|
|
||||||
|
m_minLuminance = RULE->minLuminance;
|
||||||
|
m_maxLuminance = RULE->maxLuminance;
|
||||||
|
m_maxAvgLuminance = RULE->maxAvgLuminance;
|
||||||
|
|
||||||
|
applyCMType(m_cmType, m_sdrEotf);
|
||||||
|
|
||||||
|
m_sdrSaturation = RULE->sdrSaturation;
|
||||||
|
m_sdrBrightness = RULE->sdrBrightness;
|
||||||
|
|
||||||
Vector2D logicalSize = m_pixelSize / m_scale;
|
Vector2D logicalSize = m_pixelSize / m_scale;
|
||||||
if (!*PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) {
|
if (!*PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) {
|
||||||
// invalid scale, will produce fractional pixels.
|
// invalid scale, will produce fractional pixels.
|
||||||
|
|
@ -1054,8 +1023,6 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
|
||||||
|
|
||||||
m_damage.setSize(m_transformedSize);
|
m_damage.setSize(m_transformedSize);
|
||||||
|
|
||||||
updateVCGTRamps();
|
|
||||||
|
|
||||||
// Set scale for all surfaces on this monitor, needed for some clients
|
// Set scale for all surfaces on this monitor, needed for some clients
|
||||||
// but not on unsafe state to avoid crashes
|
// but not on unsafe state to avoid crashes
|
||||||
if (!g_pCompositor->m_unsafeState) {
|
if (!g_pCompositor->m_unsafeState) {
|
||||||
|
|
@ -1362,7 +1329,7 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo
|
||||||
// move pinned windows
|
// move pinned windows
|
||||||
for (auto const& w : g_pCompositor->m_windows) {
|
for (auto const& w : g_pCompositor->m_windows) {
|
||||||
if (w->m_workspace == POLDWORKSPACE && w->m_pinned)
|
if (w->m_workspace == POLDWORKSPACE && w->m_pinned)
|
||||||
w->layoutTarget()->assignToSpace(pWorkspace->m_space);
|
w->moveToWorkspace(pWorkspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!noFocus && !Desktop::focusState()->monitor()->m_activeSpecialWorkspace &&
|
if (!noFocus && !Desktop::focusState()->monitor()->m_activeSpecialWorkspace &&
|
||||||
|
|
@ -1481,7 +1448,6 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
|
||||||
if (const auto PMWSOWNER = pWorkspace->m_monitor.lock(); PMWSOWNER && PMWSOWNER->m_activeSpecialWorkspace == pWorkspace) {
|
if (const auto PMWSOWNER = pWorkspace->m_monitor.lock(); PMWSOWNER && PMWSOWNER->m_activeSpecialWorkspace == pWorkspace) {
|
||||||
PMWSOWNER->m_activeSpecialWorkspace.reset();
|
PMWSOWNER->m_activeSpecialWorkspace.reset();
|
||||||
g_layoutManager->recalculateMonitor(PMWSOWNER);
|
g_layoutManager->recalculateMonitor(PMWSOWNER);
|
||||||
g_pHyprRenderer->damageMonitor(PMWSOWNER);
|
|
||||||
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMWSOWNER->m_name});
|
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMWSOWNER->m_name});
|
||||||
g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + PMWSOWNER->m_name});
|
g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + PMWSOWNER->m_name});
|
||||||
|
|
||||||
|
|
@ -1579,20 +1545,10 @@ Vector2D CMonitor::middle() {
|
||||||
return m_position + m_size / 2.f;
|
return m_position + m_size / 2.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Mat3x3& CMonitor::getTransformMatrix() {
|
|
||||||
return m_projMatrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Mat3x3& CMonitor::getScaleMatrix() {
|
|
||||||
return m_projOutputMatrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CMonitor::updateMatrix() {
|
void CMonitor::updateMatrix() {
|
||||||
m_projMatrix = Mat3x3::identity();
|
m_projMatrix = Mat3x3::identity();
|
||||||
if (m_transform != WL_OUTPUT_TRANSFORM_NORMAL)
|
if (m_transform != WL_OUTPUT_TRANSFORM_NORMAL)
|
||||||
m_projMatrix.translate(m_pixelSize / 2.0).transform(Math::wlTransformToHyprutils(m_transform)).translate(-m_transformedSize / 2.0);
|
m_projMatrix.translate(m_pixelSize / 2.0).transform(Math::wlTransformToHyprutils(m_transform)).translate(-m_transformedSize / 2.0);
|
||||||
|
|
||||||
m_projOutputMatrix = Mat3x3::outputProjection(m_pixelSize, HYPRUTILS_TRANSFORM_NORMAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WORKSPACEID CMonitor::activeWorkspaceID() {
|
WORKSPACEID CMonitor::activeWorkspaceID() {
|
||||||
|
|
@ -1879,7 +1835,7 @@ uint16_t CMonitor::isDSBlocked(bool full) {
|
||||||
|
|
||||||
// we can't scanout shm buffers.
|
// we can't scanout shm buffers.
|
||||||
const auto params = PSURFACE->m_current.buffer->dmabuf();
|
const auto params = PSURFACE->m_current.buffer->dmabuf();
|
||||||
if (!params.success || !PSURFACE->m_current.texture->isDMA() /* dmabuf */) {
|
if (!params.success || !PSURFACE->m_current.texture->m_eglImage /* dmabuf */) {
|
||||||
reasons |= DS_BLOCK_DMA;
|
reasons |= DS_BLOCK_DMA;
|
||||||
if (!full)
|
if (!full)
|
||||||
return reasons;
|
return reasons;
|
||||||
|
|
@ -2216,8 +2172,8 @@ bool CMonitor::canNoShaderCM() {
|
||||||
|
|
||||||
const auto SRC_DESC_VALUE = SRC_DESC.value()->value();
|
const auto SRC_DESC_VALUE = SRC_DESC.value()->value();
|
||||||
|
|
||||||
if (m_imageDescription->value().icc.present)
|
if (SRC_DESC_VALUE.icc.fd >= 0 || m_imageDescription->value().icc.fd >= 0)
|
||||||
return false;
|
return false; // no ICC support
|
||||||
|
|
||||||
const auto sdrEOTF = NTransferFunction::fromConfig();
|
const auto sdrEOTF = NTransferFunction::fromConfig();
|
||||||
// only primaries differ
|
// only primaries differ
|
||||||
|
|
@ -2236,71 +2192,6 @@ bool CMonitor::doesNoShaderCM() {
|
||||||
return m_noShaderCTM;
|
return m_noShaderCTM;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<uint16_t> resampleInterleavedToKms(const SVCGTTable16& t, size_t gammaSize) {
|
|
||||||
std::vector<uint16_t> out;
|
|
||||||
out.resize(gammaSize * 3);
|
|
||||||
|
|
||||||
//
|
|
||||||
auto sample = [&](int c, float x) -> uint16_t {
|
|
||||||
const float maxX = t.entries - 1;
|
|
||||||
x = std::clamp(x, 0.F, maxX);
|
|
||||||
|
|
||||||
const size_t i0 = (size_t)std::floor(x);
|
|
||||||
const size_t i1 = std::min(i0 + 1, (size_t)t.entries - 1);
|
|
||||||
const float f = x - sc<float>(i0);
|
|
||||||
|
|
||||||
const float v0 = sc<float>(t.ch[c][i0]);
|
|
||||||
const float v1 = sc<float>(t.ch[c][i1]);
|
|
||||||
const float v = v0 + ((v1 - v0) * f);
|
|
||||||
|
|
||||||
int64_t vi = std::round(v);
|
|
||||||
vi = std::clamp(vi, sc<int64_t>(0), sc<int64_t>(65535));
|
|
||||||
return sc<uint16_t>(vi);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (size_t i = 0; i < gammaSize; ++i) {
|
|
||||||
float x = sc<float>(i) * sc<float>(t.entries - 1) / sc<float>(gammaSize - 1);
|
|
||||||
|
|
||||||
const uint16_t r = sample(0, x);
|
|
||||||
const uint16_t g = sample(1, x);
|
|
||||||
const uint16_t b = sample(2, x);
|
|
||||||
|
|
||||||
out[i * 3 + 0] = r;
|
|
||||||
out[i * 3 + 1] = g;
|
|
||||||
out[i * 3 + 2] = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CMonitor::updateVCGTRamps() {
|
|
||||||
auto gammaSize = m_output->getGammaSize();
|
|
||||||
|
|
||||||
if (gammaSize <= 10) {
|
|
||||||
Log::logger->log(Log::DEBUG, "CMonitor::updateVCGTRamps: skipping, no gamma ramp for output");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_imageDescription->value().icc.vcgt) {
|
|
||||||
if (m_vcgtRampsSet)
|
|
||||||
m_output->state->setGammaLut({});
|
|
||||||
|
|
||||||
m_vcgtRampsSet = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// build table
|
|
||||||
auto table = resampleInterleavedToKms(*m_imageDescription->value().icc.vcgt, gammaSize);
|
|
||||||
|
|
||||||
m_output->state->setGammaLut(table);
|
|
||||||
|
|
||||||
m_vcgtRampsSet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CMonitor::gammaRampsInUse() {
|
|
||||||
return m_vcgtRampsSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
CMonitorState::CMonitorState(CMonitor* owner) : m_owner(owner) {
|
CMonitorState::CMonitorState(CMonitor* owner) : m_owner(owner) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
#include "math/Math.hpp"
|
#include "math/Math.hpp"
|
||||||
#include "../desktop/reserved/ReservedArea.hpp"
|
#include "../desktop/reserved/ReservedArea.hpp"
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include "cm/ColorManagement.hpp"
|
#include "../protocols/types/ColorManagement.hpp"
|
||||||
#include "signal/Signal.hpp"
|
#include "signal/Signal.hpp"
|
||||||
#include "DamageRing.hpp"
|
#include "DamageRing.hpp"
|
||||||
#include <aquamarine/output/Output.hpp>
|
#include <aquamarine/output/Output.hpp>
|
||||||
|
|
@ -56,7 +56,6 @@ struct SMonitorRule {
|
||||||
float sdrSaturation = 1.0f; // SDR -> HDR
|
float sdrSaturation = 1.0f; // SDR -> HDR
|
||||||
float sdrBrightness = 1.0f; // SDR -> HDR
|
float sdrBrightness = 1.0f; // SDR -> HDR
|
||||||
Desktop::CReservedArea reservedArea;
|
Desktop::CReservedArea reservedArea;
|
||||||
std::string iccFile;
|
|
||||||
|
|
||||||
int supportsWideColor = 0; // 0 - auto, 1 - force enable, -1 - force disable
|
int supportsWideColor = 0; // 0 - auto, 1 - force enable, -1 - force disable
|
||||||
int supportsHDR = 0; // 0 - auto, 1 - force enable, -1 - force disable
|
int supportsHDR = 0; // 0 - auto, 1 - force enable, -1 - force disable
|
||||||
|
|
@ -127,7 +126,7 @@ class CMonitor {
|
||||||
bool m_scheduledRecalc = false;
|
bool m_scheduledRecalc = false;
|
||||||
wl_output_transform m_transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
wl_output_transform m_transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||||
float m_xwaylandScale = 1.f;
|
float m_xwaylandScale = 1.f;
|
||||||
|
Mat3x3 m_projMatrix;
|
||||||
std::optional<Vector2D> m_forceSize;
|
std::optional<Vector2D> m_forceSize;
|
||||||
SP<Aquamarine::SOutputMode> m_currentMode;
|
SP<Aquamarine::SOutputMode> m_currentMode;
|
||||||
SP<Aquamarine::CSwapchain> m_cursorSwapchain;
|
SP<Aquamarine::CSwapchain> m_cursorSwapchain;
|
||||||
|
|
@ -303,6 +302,7 @@ class CMonitor {
|
||||||
void setSpecialWorkspace(const WORKSPACEID& id);
|
void setSpecialWorkspace(const WORKSPACEID& id);
|
||||||
void moveTo(const Vector2D& pos);
|
void moveTo(const Vector2D& pos);
|
||||||
Vector2D middle();
|
Vector2D middle();
|
||||||
|
void updateMatrix();
|
||||||
WORKSPACEID activeWorkspaceID();
|
WORKSPACEID activeWorkspaceID();
|
||||||
WORKSPACEID activeSpecialWorkspaceID();
|
WORKSPACEID activeSpecialWorkspaceID();
|
||||||
CBox logicalBox();
|
CBox logicalBox();
|
||||||
|
|
@ -332,11 +332,6 @@ class CMonitor {
|
||||||
bool wantsHDR();
|
bool wantsHDR();
|
||||||
|
|
||||||
bool inHDR();
|
bool inHDR();
|
||||||
bool gammaRampsInUse();
|
|
||||||
|
|
||||||
//
|
|
||||||
const Mat3x3& getTransformMatrix();
|
|
||||||
const Mat3x3& getScaleMatrix();
|
|
||||||
|
|
||||||
/// Has an active workspace with a real fullscreen window (includes special workspace)
|
/// Has an active workspace with a real fullscreen window (includes special workspace)
|
||||||
bool inFullscreenMode();
|
bool inFullscreenMode();
|
||||||
|
|
@ -358,8 +353,8 @@ class CMonitor {
|
||||||
PHLWINDOWREF m_previousFSWindow;
|
PHLWINDOWREF m_previousFSWindow;
|
||||||
bool m_needsHDRupdate = false;
|
bool m_needsHDRupdate = false;
|
||||||
|
|
||||||
NColorManagement::PImageDescription m_imageDescription = NColorManagement::CImageDescription::from(NColorManagement::SImageDescription{});
|
NColorManagement::PImageDescription m_imageDescription;
|
||||||
bool m_noShaderCTM = false; // sets drm CTM, restore needed
|
bool m_noShaderCTM = false; // sets drm CTM, restore needed
|
||||||
|
|
||||||
// For the list lookup
|
// For the list lookup
|
||||||
|
|
||||||
|
|
@ -367,19 +362,12 @@ class CMonitor {
|
||||||
return m_position == rhs.m_position && m_size == rhs.m_size && m_name == rhs.m_name;
|
return m_position == rhs.m_position && m_size == rhs.m_size && m_name == rhs.m_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat3x3 m_projMatrix;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateMatrix();
|
|
||||||
Mat3x3 m_projOutputMatrix;
|
|
||||||
|
|
||||||
void setupDefaultWS(const SMonitorRule&);
|
void setupDefaultWS(const SMonitorRule&);
|
||||||
WORKSPACEID findAvailableDefaultWS();
|
WORKSPACEID findAvailableDefaultWS();
|
||||||
void commitDPMSState(bool state);
|
void commitDPMSState(bool state);
|
||||||
void updateVCGTRamps();
|
|
||||||
|
|
||||||
bool m_doneScheduled = false;
|
bool m_doneScheduled = false;
|
||||||
bool m_vcgtRampsSet = false;
|
|
||||||
std::stack<WORKSPACEID> m_prevWorkSpaces;
|
std::stack<WORKSPACEID> m_prevWorkSpaces;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,7 @@ std::string NTransferFunction::toString(eTF tf) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
eTF NTransferFunction::fromConfig(bool useICC) {
|
eTF NTransferFunction::fromConfig() {
|
||||||
if (useICC)
|
|
||||||
return TF_SRGB;
|
|
||||||
|
|
||||||
static auto PSDREOTF = CConfigValue<Hyprlang::STRING>("render:cm_sdr_eotf");
|
static auto PSDREOTF = CConfigValue<Hyprlang::STRING>("render:cm_sdr_eotf");
|
||||||
static auto sdrEOTF = NTransferFunction::fromString(*PSDREOTF);
|
static auto sdrEOTF = NTransferFunction::fromString(*PSDREOTF);
|
||||||
static auto P = Event::bus()->m_events.config.reloaded.listen([]() { sdrEOTF = NTransferFunction::fromString(*PSDREOTF); });
|
static auto P = Event::bus()->m_events.config.reloaded.listen([]() { sdrEOTF = NTransferFunction::fromString(*PSDREOTF); });
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,5 @@ namespace NTransferFunction {
|
||||||
eTF fromString(const std::string tfName);
|
eTF fromString(const std::string tfName);
|
||||||
std::string toString(eTF tf);
|
std::string toString(eTF tf);
|
||||||
|
|
||||||
eTF fromConfig(bool useICC = false);
|
eTF fromConfig();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,193 +0,0 @@
|
||||||
#include "ColorManagement.hpp"
|
|
||||||
#include "../../macros.hpp"
|
|
||||||
#include <hyprutils/memory/UniquePtr.hpp>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace NColorManagement;
|
|
||||||
|
|
||||||
namespace NColorManagement {
|
|
||||||
// expected to be small
|
|
||||||
static std::vector<UP<const CPrimaries>> knownPrimaries;
|
|
||||||
static std::vector<UP<const CImageDescription>> knownDescriptions;
|
|
||||||
static std::map<std::pair<uint, uint>, Hyprgraphics::CMatrix3> primariesConversion;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SPCPRimaries& NColorManagement::getPrimaries(ePrimaries name) {
|
|
||||||
switch (name) {
|
|
||||||
case CM_PRIMARIES_SRGB: return NColorPrimaries::BT709;
|
|
||||||
case CM_PRIMARIES_BT2020: return NColorPrimaries::BT2020;
|
|
||||||
case CM_PRIMARIES_PAL_M: return NColorPrimaries::PAL_M;
|
|
||||||
case CM_PRIMARIES_PAL: return NColorPrimaries::PAL;
|
|
||||||
case CM_PRIMARIES_NTSC: return NColorPrimaries::NTSC;
|
|
||||||
case CM_PRIMARIES_GENERIC_FILM: return NColorPrimaries::GENERIC_FILM;
|
|
||||||
case CM_PRIMARIES_CIE1931_XYZ: return NColorPrimaries::CIE1931_XYZ;
|
|
||||||
case CM_PRIMARIES_DCI_P3: return NColorPrimaries::DCI_P3;
|
|
||||||
case CM_PRIMARIES_DISPLAY_P3: return NColorPrimaries::DISPLAY_P3;
|
|
||||||
case CM_PRIMARIES_ADOBE_RGB: return NColorPrimaries::ADOBE_RGB;
|
|
||||||
default: return NColorPrimaries::DEFAULT_PRIMARIES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPrimaries::CPrimaries(const SPCPRimaries& primaries, const uint32_t primariesId) : m_id(primariesId), m_primaries(primaries) {
|
|
||||||
m_primaries2XYZ = m_primaries.toXYZ();
|
|
||||||
}
|
|
||||||
|
|
||||||
WP<const CPrimaries> CPrimaries::from(const SPCPRimaries& primaries) {
|
|
||||||
for (const auto& known : knownPrimaries) {
|
|
||||||
if (known->value() == primaries)
|
|
||||||
return known;
|
|
||||||
}
|
|
||||||
|
|
||||||
knownPrimaries.emplace_back(CUniquePointer(new CPrimaries(primaries, knownPrimaries.size() + 1)));
|
|
||||||
return knownPrimaries.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
WP<const CPrimaries> CPrimaries::from(const ePrimaries name) {
|
|
||||||
return from(getPrimaries(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
WP<const CPrimaries> CPrimaries::from(const uint32_t primariesId) {
|
|
||||||
ASSERT(primariesId <= knownPrimaries.size());
|
|
||||||
return knownPrimaries[primariesId - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
const SPCPRimaries& CPrimaries::value() const {
|
|
||||||
return m_primaries;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint CPrimaries::id() const {
|
|
||||||
return m_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Hyprgraphics::CMatrix3& CPrimaries::toXYZ() const {
|
|
||||||
return m_primaries2XYZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Hyprgraphics::CMatrix3& CPrimaries::convertMatrix(const WP<const CPrimaries> dst) const {
|
|
||||||
const auto cacheKey = std::make_pair(m_id, dst->m_id);
|
|
||||||
if (!primariesConversion.contains(cacheKey))
|
|
||||||
primariesConversion.insert(std::make_pair(cacheKey, m_primaries.convertMatrix(dst->m_primaries)));
|
|
||||||
|
|
||||||
return primariesConversion[cacheKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
CImageDescription::CImageDescription(const SImageDescription& imageDescription, const uint32_t imageDescriptionId) :
|
|
||||||
m_id(imageDescriptionId), m_imageDescription(imageDescription) {
|
|
||||||
m_primariesId = CPrimaries::from(m_imageDescription.getPrimaries())->id();
|
|
||||||
}
|
|
||||||
|
|
||||||
PImageDescription CImageDescription::from(const SImageDescription& imageDescription) {
|
|
||||||
for (const auto& known : knownDescriptions) {
|
|
||||||
if (known->value() == imageDescription)
|
|
||||||
return known;
|
|
||||||
}
|
|
||||||
|
|
||||||
knownDescriptions.emplace_back(UP<CImageDescription>(new CImageDescription(imageDescription, knownDescriptions.size() + 1)));
|
|
||||||
return knownDescriptions.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
PImageDescription CImageDescription::from(const uint32_t imageDescriptionId) {
|
|
||||||
ASSERT(imageDescriptionId <= knownDescriptions.size());
|
|
||||||
return knownDescriptions[imageDescriptionId - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
PImageDescription CImageDescription::with(const SImageDescription::SPCLuminances& luminances) const {
|
|
||||||
auto desc = m_imageDescription;
|
|
||||||
desc.luminances = luminances;
|
|
||||||
return CImageDescription::from(desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
const SImageDescription& CImageDescription::value() const {
|
|
||||||
return m_imageDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint CImageDescription::id() const {
|
|
||||||
return m_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
WP<const CPrimaries> CImageDescription::getPrimaries() const {
|
|
||||||
return CPrimaries::from(m_primariesId);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Mat3x3 diag3(const std::array<float, 3>& s) {
|
|
||||||
return Mat3x3{std::array<float, 9>{s[0], 0, 0, 0, s[1], 0, 0, 0, s[2]}};
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::optional<Mat3x3> invertMat3(const Mat3x3& m) {
|
|
||||||
const auto ARR = m.getMatrix();
|
|
||||||
const double a = ARR[0], b = ARR[1], c = ARR[2];
|
|
||||||
const double d = ARR[3], e = ARR[4], f = ARR[5];
|
|
||||||
const double g = ARR[6], h = ARR[7], i = ARR[8];
|
|
||||||
|
|
||||||
const double A = (e * i - f * h);
|
|
||||||
const double B = -(d * i - f * g);
|
|
||||||
const double C = (d * h - e * g);
|
|
||||||
const double D = -(b * i - c * h);
|
|
||||||
const double E = (a * i - c * g);
|
|
||||||
const double F = -(a * h - b * g);
|
|
||||||
const double G = (b * f - c * e);
|
|
||||||
const double H = -(a * f - c * d);
|
|
||||||
const double I = (a * e - b * d);
|
|
||||||
|
|
||||||
const double det = a * A + b * B + c * C;
|
|
||||||
if (std::abs(det) < 1e-18)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
const double invDet = 1.0 / det;
|
|
||||||
Mat3x3 inv{std::array<float, 9>{
|
|
||||||
A * invDet,
|
|
||||||
D * invDet,
|
|
||||||
G * invDet, //
|
|
||||||
B * invDet,
|
|
||||||
E * invDet,
|
|
||||||
H * invDet, //
|
|
||||||
C * invDet,
|
|
||||||
F * invDet,
|
|
||||||
I * invDet, //
|
|
||||||
}};
|
|
||||||
return inv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::array<float, 3> matByVec(const Mat3x3& M, const std::array<float, 3>& v) {
|
|
||||||
const auto ARR = M.getMatrix();
|
|
||||||
return {ARR[0] * v[0] + ARR[1] * v[1] + ARR[2] * v[2], ARR[3] * v[0] + ARR[4] * v[1] + ARR[5] * v[2], ARR[6] * v[0] + ARR[7] * v[1] + ARR[8] * v[2]};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<Mat3x3> NColorManagement::rgbToXYZFromPrimaries(SPCPRimaries pr) {
|
|
||||||
const auto R = Hyprgraphics::xy2xyz(pr.red);
|
|
||||||
const auto G = Hyprgraphics::xy2xyz(pr.green);
|
|
||||||
const auto B = Hyprgraphics::xy2xyz(pr.blue);
|
|
||||||
const auto W = Hyprgraphics::xy2xyz(pr.white);
|
|
||||||
|
|
||||||
// P has columns R,G,B
|
|
||||||
Mat3x3 P{std::array<float, 9>{R.x, G.x, B.x, R.y, G.y, B.y, R.z, G.z, B.z}};
|
|
||||||
|
|
||||||
auto invP = invertMat3(P);
|
|
||||||
if (!invP)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
const auto S = matByVec(*invP, {W.x, W.y, W.z});
|
|
||||||
|
|
||||||
P.multiply(diag3(S)); // RGB->XYZ
|
|
||||||
|
|
||||||
return P;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3x3 NColorManagement::adaptBradford(Hyprgraphics::CColor::xy srcW, Hyprgraphics::CColor::xy dstW) {
|
|
||||||
static const Mat3x3 Bradford{std::array<float, 9>{0.8951f, 0.2664f, -0.1614f, -0.7502f, 1.7135f, 0.0367f, 0.0389f, -0.0685f, 1.0296f}};
|
|
||||||
static const Mat3x3 BradfordInv = invertMat3(Bradford).value();
|
|
||||||
|
|
||||||
const auto srcXYZ = Hyprgraphics::xy2xyz(srcW);
|
|
||||||
const auto dstXYZ = Hyprgraphics::xy2xyz(dstW);
|
|
||||||
|
|
||||||
const auto srcLMS = matByVec(Bradford, {srcXYZ.x, srcXYZ.y, srcXYZ.z});
|
|
||||||
const auto dstLMS = matByVec(Bradford, {dstXYZ.x, dstXYZ.y, dstXYZ.z});
|
|
||||||
|
|
||||||
const std::array<float, 3> scale{dstLMS[0] / srcLMS[0], dstLMS[1] / srcLMS[1], dstLMS[2] / srcLMS[2]};
|
|
||||||
|
|
||||||
Mat3x3 result = BradfordInv;
|
|
||||||
result.multiply(diag3(scale)).multiply(Bradford);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
@ -1,278 +0,0 @@
|
||||||
#include "ColorManagement.hpp"
|
|
||||||
#include "../math/Math.hpp"
|
|
||||||
#include <cstddef>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include "../../debug/log/Logger.hpp"
|
|
||||||
#include "../../render/Texture.hpp"
|
|
||||||
#include "../../render/Renderer.hpp"
|
|
||||||
|
|
||||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
|
||||||
using namespace Hyprutils::Utils;
|
|
||||||
|
|
||||||
#include <lcms2.h>
|
|
||||||
|
|
||||||
using namespace NColorManagement;
|
|
||||||
|
|
||||||
static std::vector<uint8_t> readBinary(const std::filesystem::path& file) {
|
|
||||||
std::ifstream ifs(file, std::ios::binary);
|
|
||||||
if (!ifs.good())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
ifs.seekg(0, std::ios::end);
|
|
||||||
size_t len = ifs.tellg();
|
|
||||||
ifs.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
if (len <= 0)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
std::vector<uint8_t> buf;
|
|
||||||
buf.resize(len);
|
|
||||||
ifs.read(reinterpret_cast<char*>(buf.data()), len);
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t bigEndianU16(const uint8_t* p) {
|
|
||||||
return (uint16_t)((uint16_t)p[0] << 8 | (uint16_t)p[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t bigEndianU32(const uint8_t* p) {
|
|
||||||
return (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | (uint32_t)p[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr cmsTagSignature makeSig(char a, char b, char c, char d) {
|
|
||||||
return sc<cmsTagSignature>(sc<uint32_t>(a) << 24 | sc<uint32_t>(b) << 16 | sc<uint32_t>(c) << 8 | sc<uint32_t>(d));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr cmsTagSignature VCGT_SIG = makeSig('v', 'c', 'g', 't');
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
static std::expected<std::optional<SVCGTTable16>, std::string> readVCGT16(cmsHPROFILE prof) {
|
|
||||||
if (!cmsIsTag(prof, VCGT_SIG))
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
cmsUInt32Number n = cmsReadRawTag(prof, VCGT_SIG, nullptr, 0);
|
|
||||||
if (n < 8 + 4 + 2 + 2 + 2 + 2) // header + type + table header
|
|
||||||
return std::unexpected("Malformed vcgt tag");
|
|
||||||
|
|
||||||
std::vector<uint8_t> raw(n);
|
|
||||||
if (cmsReadRawTag(prof, VCGT_SIG, raw.data(), n) != n)
|
|
||||||
return std::unexpected("Malformed vcgt tag");
|
|
||||||
|
|
||||||
// raw layout:
|
|
||||||
// 0 ... 3: 'vcgt'
|
|
||||||
// 4 ... 7: reserved
|
|
||||||
// 8 ... 11: gammaType (0 = table)
|
|
||||||
uint32_t gammaType = bigEndianU32(raw.data() + 8);
|
|
||||||
if (gammaType != 0)
|
|
||||||
return std::unexpected("VCGT formula type is not supported by Hyprland");
|
|
||||||
|
|
||||||
SVCGTTable16 table;
|
|
||||||
table.channels = bigEndianU16(raw.data() + 12);
|
|
||||||
table.entries = bigEndianU16(raw.data() + 14);
|
|
||||||
table.entrySize = bigEndianU16(raw.data() + 16);
|
|
||||||
// raw+18: reserved u16
|
|
||||||
|
|
||||||
Log::logger->log(Log::DEBUG, "readVCGT16: table has {} channels, {} entries, and entry size of {}", table.channels, table.entries, table.entrySize);
|
|
||||||
|
|
||||||
if (table.channels != 3 || table.entrySize != 2 || table.entries == 0)
|
|
||||||
return std::unexpected("invalid vcgt table size");
|
|
||||||
|
|
||||||
size_t tableBytes = (size_t)table.channels * (size_t)table.entries * (size_t)table.entrySize;
|
|
||||||
|
|
||||||
// VCGT is a piece of shit and some absolute fucking mongoloid idiots
|
|
||||||
// decided it'd be great to have both 18 and 20
|
|
||||||
// FUCK YOU
|
|
||||||
size_t tableOff = 20;
|
|
||||||
|
|
||||||
auto readTable = [&] -> void {
|
|
||||||
for (int c = 0; c < 3; ++c) {
|
|
||||||
table.ch[c].resize(table.entries);
|
|
||||||
for (uint16_t i = 0; i < table.entries; ++i) {
|
|
||||||
const uint8_t* p = raw.data() + tableOff + static_cast<ptrdiff_t>((c * table.entries + i) * 2);
|
|
||||||
table.ch[c][i] = bigEndianU16(p); // 0 ... 65535
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (raw.size() < tableOff + tableBytes) {
|
|
||||||
tableOff = 18;
|
|
||||||
|
|
||||||
if (raw.size() < tableOff + tableBytes) {
|
|
||||||
Log::logger->log(Log::ERR, "readVCGT16: table is too short, tag is invalid");
|
|
||||||
return std::unexpected("table is too short");
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::logger->log(Log::DEBUG, "readVCGT16: table is too short, but off = 18 fits. Attempting offset = 18");
|
|
||||||
|
|
||||||
readTable();
|
|
||||||
} else {
|
|
||||||
readTable();
|
|
||||||
|
|
||||||
// if the table's last entry is suspiciously low, we more than likely read an 18 as a 20.
|
|
||||||
if (table.ch[0][table.entries - 1] < 30000) {
|
|
||||||
Log::logger->log(Log::DEBUG, "readVCGT16: table is likely offset 18 not 20, re-reading");
|
|
||||||
|
|
||||||
tableOff = 18;
|
|
||||||
|
|
||||||
readTable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (table.ch[0][table.entries - 1] < 30000) {
|
|
||||||
Log::logger->log(Log::ERR, "readVCGT16: table is malformed, last value of a gamma ramp can't be {}", table.ch[0][table.entries - 1]);
|
|
||||||
return std::unexpected("invalid table values");
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::logger->log(Log::DEBUG, "readVCGT16: red channel: [{}, {}, ... {}, {}]", table.ch[0][0], table.ch[0][1], table.ch[0][table.entries - 2], table.ch[0][table.entries - 1]);
|
|
||||||
Log::logger->log(Log::DEBUG, "readVCGT16: green channel: [{}, {}, ... {}, {}]", table.ch[1][0], table.ch[1][1], table.ch[1][table.entries - 2], table.ch[1][table.entries - 1]);
|
|
||||||
Log::logger->log(Log::DEBUG, "readVCGT16: blue channel: [{}, {}, ... {}, {}]", table.ch[2][0], table.ch[2][1], table.ch[2][table.entries - 2], table.ch[2][table.entries - 1]);
|
|
||||||
|
|
||||||
return table;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CmsProfileDeleter {
|
|
||||||
void operator()(cmsHPROFILE p) const {
|
|
||||||
if (p)
|
|
||||||
cmsCloseProfile(p);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct CmsTransformDeleter {
|
|
||||||
void operator()(cmsHTRANSFORM t) const {
|
|
||||||
if (t)
|
|
||||||
cmsDeleteTransform(t);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using UniqueProfile = std::unique_ptr<std::remove_pointer_t<cmsHPROFILE>, CmsProfileDeleter>;
|
|
||||||
using UniqueTransform = std::unique_ptr<std::remove_pointer_t<cmsHTRANSFORM>, CmsTransformDeleter>;
|
|
||||||
|
|
||||||
static UniqueProfile createLinearSRGBProfile() {
|
|
||||||
cmsCIExyYTRIPLE prim{};
|
|
||||||
// sRGB / Rec.709 primaries
|
|
||||||
prim.Red.x = 0.6400;
|
|
||||||
prim.Red.y = 0.3300;
|
|
||||||
prim.Red.Y = 1.0;
|
|
||||||
prim.Green.x = 0.3000;
|
|
||||||
prim.Green.y = 0.6000;
|
|
||||||
prim.Green.Y = 1.0;
|
|
||||||
prim.Blue.x = 0.1500;
|
|
||||||
prim.Blue.y = 0.0600;
|
|
||||||
prim.Blue.Y = 1.0;
|
|
||||||
|
|
||||||
cmsCIExyY wp{};
|
|
||||||
wp.x = 0.3127;
|
|
||||||
wp.y = 0.3290;
|
|
||||||
wp.Y = 1.0; // D65
|
|
||||||
|
|
||||||
cmsToneCurve* lin = cmsBuildGamma(nullptr, 1.0);
|
|
||||||
cmsToneCurve* curves[3] = {lin, lin, lin};
|
|
||||||
|
|
||||||
cmsHPROFILE p = cmsCreateRGBProfile(&wp, &prim, curves);
|
|
||||||
|
|
||||||
cmsFreeToneCurve(lin);
|
|
||||||
return UniqueProfile{p};
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::expected<void, std::string> buildIcc3DLut(cmsHPROFILE profile, SImageDescription& image) {
|
|
||||||
UniqueProfile src = createLinearSRGBProfile();
|
|
||||||
if (!src)
|
|
||||||
return std::unexpected("Failed to create linear sRGB profile");
|
|
||||||
|
|
||||||
// Rendering intent: RELATIVE_COLORIMETRIC is common for displays; add BPC to be safe.
|
|
||||||
const int intent = INTENT_RELATIVE_COLORIMETRIC;
|
|
||||||
const cmsUInt32Number flags = cmsFLAGS_BLACKPOINTCOMPENSATION | cmsFLAGS_HIGHRESPRECALC; // good quality precalc in LCMS
|
|
||||||
|
|
||||||
// float->float transform (linear input, encoded output in dst device space)
|
|
||||||
UniqueTransform xform{cmsCreateTransform(src.get(), TYPE_RGB_FLT, profile, TYPE_RGB_FLT, intent, flags)};
|
|
||||||
if (!xform)
|
|
||||||
return std::unexpected("Failed to create ICC transform");
|
|
||||||
|
|
||||||
Log::logger->log(Log::DEBUG, "Building a {}³ 3D LUT", image.icc.lutSize);
|
|
||||||
|
|
||||||
image.icc.present = true;
|
|
||||||
image.icc.lutDataPacked.resize(image.icc.lutSize * image.icc.lutSize * image.icc.lutSize * 3);
|
|
||||||
|
|
||||||
auto idx = [&image](int r, int g, int b) -> size_t {
|
|
||||||
//
|
|
||||||
return ((size_t)b * image.icc.lutSize * image.icc.lutSize + (size_t)g * image.icc.lutSize + (size_t)r) * 3;
|
|
||||||
};
|
|
||||||
|
|
||||||
for (size_t bz = 0; bz < image.icc.lutSize; ++bz) {
|
|
||||||
for (size_t gy = 0; gy < image.icc.lutSize; ++gy) {
|
|
||||||
for (size_t rx = 0; rx < image.icc.lutSize; ++rx) {
|
|
||||||
float in[3] = {
|
|
||||||
rx / float(image.icc.lutSize - 1),
|
|
||||||
gy / float(image.icc.lutSize - 1),
|
|
||||||
bz / float(image.icc.lutSize - 1),
|
|
||||||
};
|
|
||||||
float outRGB[3];
|
|
||||||
cmsDoTransform(xform.get(), in, outRGB, 1);
|
|
||||||
|
|
||||||
outRGB[0] = std::clamp(outRGB[0], 0.F, 1.F);
|
|
||||||
outRGB[1] = std::clamp(outRGB[1], 0.F, 1.F);
|
|
||||||
outRGB[2] = std::clamp(outRGB[2], 0.F, 1.F);
|
|
||||||
|
|
||||||
const size_t o = idx(rx, gy, bz);
|
|
||||||
image.icc.lutDataPacked[o + 0] = outRGB[0];
|
|
||||||
image.icc.lutDataPacked[o + 1] = outRGB[1];
|
|
||||||
image.icc.lutDataPacked[o + 2] = outRGB[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::logger->log(Log::DEBUG, "3D LUT constructed, size {}", image.icc.lutDataPacked.size());
|
|
||||||
|
|
||||||
// upload
|
|
||||||
image.icc.lutTexture = g_pHyprRenderer->createTexture(image.icc.lutDataPacked, image.icc.lutSize);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::expected<SImageDescription, std::string> SImageDescription::fromICC(const std::filesystem::path& file) {
|
|
||||||
static auto PVCGTENABLED = CConfigValue<Hyprlang::INT>("render:icc_vcgt_enabled");
|
|
||||||
|
|
||||||
std::error_code ec;
|
|
||||||
if (!std::filesystem::exists(file, ec) || ec)
|
|
||||||
return std::unexpected("Invalid file");
|
|
||||||
|
|
||||||
SImageDescription image;
|
|
||||||
image.rawICC = readBinary(file);
|
|
||||||
|
|
||||||
if (image.rawICC.empty())
|
|
||||||
return std::unexpected("Failed to read file");
|
|
||||||
|
|
||||||
cmsHPROFILE prof = cmsOpenProfileFromFile(file.string().c_str(), "r");
|
|
||||||
if (!prof)
|
|
||||||
return std::unexpected("CMS failed to open icc file");
|
|
||||||
|
|
||||||
CScopeGuard x([&prof] { cmsCloseProfile(prof); });
|
|
||||||
|
|
||||||
// only handle RGB (typical display profiles)
|
|
||||||
if (cmsGetColorSpace(prof) != cmsSigRgbData)
|
|
||||||
return std::unexpected("Only RGB display profiles are supported");
|
|
||||||
|
|
||||||
Log::logger->log(Log::DEBUG, "============= Begin ICC load =============");
|
|
||||||
Log::logger->log(Log::DEBUG, "ICC size: {} bytes", image.rawICC.size());
|
|
||||||
|
|
||||||
if (const auto RET = buildIcc3DLut(prof, image); !RET)
|
|
||||||
return std::unexpected(RET.error());
|
|
||||||
|
|
||||||
if (*PVCGTENABLED) {
|
|
||||||
auto vcgtRes = readVCGT16(prof);
|
|
||||||
if (!vcgtRes)
|
|
||||||
return std::unexpected(vcgtRes.error());
|
|
||||||
|
|
||||||
image.icc.vcgt = *vcgtRes;
|
|
||||||
|
|
||||||
if (!*vcgtRes)
|
|
||||||
Log::logger->log(Log::DEBUG, "ICC profile has no VCGT data");
|
|
||||||
} else
|
|
||||||
Log::logger->log(Log::DEBUG, "Skipping VCGT load, disabled by config");
|
|
||||||
|
|
||||||
Log::logger->log(Log::DEBUG, "============= End ICC load =============");
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
@ -30,6 +30,8 @@ CHyprError::CHyprError() {
|
||||||
if (m_fadeOpacity->isBeingAnimated() || m_monitorChanged)
|
if (m_fadeOpacity->isBeingAnimated() || m_monitorChanged)
|
||||||
g_pHyprRenderer->damageBox(m_damageBox);
|
g_pHyprRenderer->damageBox(m_damageBox);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_texture = makeShared<CTexture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprError::queueCreate(std::string message, const CHyprColor& color) {
|
void CHyprError::queueCreate(std::string message, const CHyprColor& color) {
|
||||||
|
|
@ -38,8 +40,8 @@ void CHyprError::queueCreate(std::string message, const CHyprColor& color) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprError::createQueued() {
|
void CHyprError::createQueued() {
|
||||||
if (m_isCreated && m_texture)
|
if (m_isCreated)
|
||||||
m_texture.reset();
|
m_texture->destroyTexture();
|
||||||
|
|
||||||
m_fadeOpacity->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn"));
|
m_fadeOpacity->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn"));
|
||||||
|
|
||||||
|
|
@ -143,13 +145,12 @@ void CHyprError::createQueued() {
|
||||||
|
|
||||||
// copy the data to an OpenGL texture we have
|
// copy the data to an OpenGL texture we have
|
||||||
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE);
|
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE);
|
||||||
auto tex = texture();
|
m_texture->allocate();
|
||||||
tex->allocate(PMONITOR->m_pixelSize);
|
m_texture->bind();
|
||||||
tex->bind();
|
m_texture->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
m_texture->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||||
tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||||
tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED);
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||||
|
|
||||||
|
|
@ -186,8 +187,7 @@ void CHyprError::draw() {
|
||||||
if (!m_fadeOpacity->isBeingAnimated()) {
|
if (!m_fadeOpacity->isBeingAnimated()) {
|
||||||
if (m_fadeOpacity->value() == 0.f) {
|
if (m_fadeOpacity->value() == 0.f) {
|
||||||
m_queuedDestroy = false;
|
m_queuedDestroy = false;
|
||||||
if (m_texture)
|
m_texture->destroyTexture();
|
||||||
m_texture.reset();
|
|
||||||
m_isCreated = false;
|
m_isCreated = false;
|
||||||
m_queued = "";
|
m_queued = "";
|
||||||
|
|
||||||
|
|
@ -218,7 +218,7 @@ void CHyprError::draw() {
|
||||||
m_monitorChanged = false;
|
m_monitorChanged = false;
|
||||||
|
|
||||||
CTexPassElement::SRenderData data;
|
CTexPassElement::SRenderData data;
|
||||||
data.tex = texture();
|
data.tex = m_texture;
|
||||||
data.box = monbox;
|
data.box = monbox;
|
||||||
data.a = m_fadeOpacity->value();
|
data.a = m_fadeOpacity->value();
|
||||||
|
|
||||||
|
|
@ -239,9 +239,3 @@ bool CHyprError::active() {
|
||||||
float CHyprError::height() {
|
float CHyprError::height() {
|
||||||
return m_lastHeight;
|
return m_lastHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
SP<ITexture> CHyprError::texture() {
|
|
||||||
if (!m_texture)
|
|
||||||
m_texture = g_pHyprRenderer->createTexture();
|
|
||||||
return m_texture;
|
|
||||||
}
|
|
||||||
|
|
@ -18,16 +18,13 @@ class CHyprError {
|
||||||
bool active();
|
bool active();
|
||||||
float height(); // logical
|
float height(); // logical
|
||||||
|
|
||||||
//
|
|
||||||
SP<ITexture> texture();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createQueued();
|
void createQueued();
|
||||||
std::string m_queued = "";
|
std::string m_queued = "";
|
||||||
CHyprColor m_queuedColor;
|
CHyprColor m_queuedColor;
|
||||||
bool m_queuedDestroy = false;
|
bool m_queuedDestroy = false;
|
||||||
bool m_isCreated = false;
|
bool m_isCreated = false;
|
||||||
SP<ITexture> m_texture;
|
SP<CTexture> m_texture;
|
||||||
PHLANIMVAR<float> m_fadeOpacity;
|
PHLANIMVAR<float> m_fadeOpacity;
|
||||||
CBox m_damageBox = {0, 0, 0, 0};
|
CBox m_damageBox = {0, 0, 0, 0};
|
||||||
float m_lastHeight = 0.F;
|
float m_lastHeight = 0.F;
|
||||||
|
|
|
||||||
|
|
@ -1605,7 +1605,6 @@ I18n::CI18nEngine::CI18nEngine() {
|
||||||
|
|
||||||
huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Ứng dụng <b>{app}</b> đang yêu cầu một quyền không xác định.");
|
huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Ứng dụng <b>{app}</b> đang yêu cầu một quyền không xác định.");
|
||||||
huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Ứng dụng <b>{app}</b> đang cố gắng ghi hình màn hình của bạn.\n\nBạn muốn cho phép không?");
|
huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Ứng dụng <b>{app}</b> đang cố gắng ghi hình màn hình của bạn.\n\nBạn muốn cho phép không?");
|
||||||
huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_CURSOR_POS, "Ứng dụng <b>{app}</b> đang cố gắng đọc vị trí chuột của bạn.\n\nBạn muốn cho phép không?");
|
|
||||||
huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Ứng dụng <b>{app}</b> đang cố gắng tải plugin: <b>{plugin}</b>.\n\nBạn muốn cho phép không?");
|
huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Ứng dụng <b>{app}</b> đang cố gắng tải plugin: <b>{plugin}</b>.\n\nBạn muốn cho phép không?");
|
||||||
huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Phát hiện bàn phím mới: <b>{keyboard}</b>.\n\nBạn muốn cho phép bàn phím này hoạt động không?");
|
huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Phát hiện bàn phím mới: <b>{keyboard}</b>.\n\nBạn muốn cho phép bàn phím này hoạt động không?");
|
||||||
huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(không xác định)");
|
huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(không xác định)");
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,13 @@
|
||||||
|
|
||||||
using namespace Layout;
|
using namespace Layout;
|
||||||
|
|
||||||
CLayoutManager::CLayoutManager() = default;
|
CLayoutManager::CLayoutManager() {
|
||||||
|
static auto P = Event::bus()->m_events.monitor.layoutChanged.listen([] {
|
||||||
|
for (const auto& ws : g_pCompositor->getWorkspaces()) {
|
||||||
|
ws->m_space->recheckWorkArea();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void CLayoutManager::newTarget(SP<ITarget> target, SP<CSpace> space) {
|
void CLayoutManager::newTarget(SP<ITarget> target, SP<CSpace> space) {
|
||||||
// on a new target: remember desired pos for float, if available
|
// on a new target: remember desired pos for float, if available
|
||||||
|
|
@ -61,13 +67,6 @@ void CLayoutManager::resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectC
|
||||||
target->space()->resizeTarget(Δ, target, corner);
|
target->space()->resizeTarget(Δ, target, corner);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLayoutManager::setTargetGeom(const CBox& box, SP<ITarget> target) {
|
|
||||||
if (!target->floating())
|
|
||||||
return;
|
|
||||||
|
|
||||||
target->space()->setTargetGeom(box, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::expected<void, std::string> CLayoutManager::layoutMsg(const std::string_view& sv) {
|
std::expected<void, std::string> CLayoutManager::layoutMsg(const std::string_view& sv) {
|
||||||
|
|
||||||
const auto MONITOR = Desktop::focusState()->monitor();
|
const auto MONITOR = Desktop::focusState()->monitor();
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@ namespace Layout {
|
||||||
void moveMouse(const Vector2D& mousePos);
|
void moveMouse(const Vector2D& mousePos);
|
||||||
void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||||
void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
||||||
void setTargetGeom(const CBox& box, SP<ITarget> target); // floats only
|
|
||||||
void endDragTarget();
|
void endDragTarget();
|
||||||
|
|
||||||
std::expected<void, std::string> layoutMsg(const std::string_view& sv);
|
std::expected<void, std::string> layoutMsg(const std::string_view& sv);
|
||||||
|
|
|
||||||
|
|
@ -42,16 +42,16 @@ void CAlgorithm::removeTarget(SP<ITarget> target) {
|
||||||
const bool IS_FLOATING = std::ranges::contains(m_floatingTargets, target);
|
const bool IS_FLOATING = std::ranges::contains(m_floatingTargets, target);
|
||||||
|
|
||||||
if (IS_FLOATING) {
|
if (IS_FLOATING) {
|
||||||
std::erase(m_floatingTargets, target);
|
|
||||||
m_floating->removeTarget(target);
|
m_floating->removeTarget(target);
|
||||||
|
std::erase(m_floatingTargets, target);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool IS_TILED = std::ranges::contains(m_tiledTargets, target);
|
const bool IS_TILED = std::ranges::contains(m_tiledTargets, target);
|
||||||
|
|
||||||
if (IS_TILED) {
|
if (IS_TILED) {
|
||||||
std::erase(m_tiledTargets, target);
|
|
||||||
m_tiled->removeTarget(target);
|
m_tiled->removeTarget(target);
|
||||||
|
std::erase(m_tiledTargets, target);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -262,10 +262,3 @@ SP<ITarget> CAlgorithm::getNextCandidate(SP<ITarget> old) {
|
||||||
// god damn it, maybe empty?
|
// god damn it, maybe empty?
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAlgorithm::setTargetGeom(const CBox& box, SP<ITarget> target) {
|
|
||||||
if (!target->floating() || !std::ranges::contains(m_floatingTargets, target))
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_floating->setTargetGeom(box, target);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,6 @@ namespace Layout {
|
||||||
void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||||
void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
||||||
|
|
||||||
void setTargetGeom(const CBox& box, SP<ITarget> target); // only for float
|
|
||||||
|
|
||||||
void updateFloatingAlgo(UP<IFloatingAlgorithm>&& algo);
|
void updateFloatingAlgo(UP<IFloatingAlgorithm>&& algo);
|
||||||
void updateTiledAlgo(UP<ITiledAlgorithm>&& algo);
|
void updateTiledAlgo(UP<ITiledAlgorithm>&& algo);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,6 @@ namespace Layout {
|
||||||
// a target is being moved by a delta
|
// a target is being moved by a delta
|
||||||
virtual void moveTarget(const Vector2D& Δ, SP<ITarget> target) = 0;
|
virtual void moveTarget(const Vector2D& Δ, SP<ITarget> target) = 0;
|
||||||
|
|
||||||
// a target is moved to a pos x size
|
|
||||||
virtual void setTargetGeom(const CBox& geom, SP<ITarget> target) = 0;
|
|
||||||
|
|
||||||
virtual void recenter(SP<ITarget> t);
|
virtual void recenter(SP<ITarget> t);
|
||||||
|
|
||||||
virtual void recalculate();
|
virtual void recalculate();
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,5 @@
|
||||||
#include "ModeAlgorithm.hpp"
|
#include "ModeAlgorithm.hpp"
|
||||||
|
|
||||||
#include "../space/Space.hpp"
|
|
||||||
#include "Algorithm.hpp"
|
|
||||||
#include "../../helpers/Monitor.hpp"
|
|
||||||
#include "../../desktop/view/Window.hpp"
|
|
||||||
|
|
||||||
using namespace Layout;
|
using namespace Layout;
|
||||||
|
|
||||||
std::expected<void, std::string> IModeAlgorithm::layoutMsg(const std::string_view& sv) {
|
std::expected<void, std::string> IModeAlgorithm::layoutMsg(const std::string_view& sv) {
|
||||||
|
|
@ -14,20 +9,3 @@ std::expected<void, std::string> IModeAlgorithm::layoutMsg(const std::string_vie
|
||||||
std::optional<Vector2D> IModeAlgorithm::predictSizeForNewTarget() {
|
std::optional<Vector2D> IModeAlgorithm::predictSizeForNewTarget() {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Vector2D> IModeAlgorithm::focalPointForDir(SP<ITarget> t, Math::eDirection dir) {
|
|
||||||
Vector2D focalPoint;
|
|
||||||
|
|
||||||
const auto WINDOWIDEALBB =
|
|
||||||
t->fullscreenMode() != FSMODE_NONE ? m_parent->space()->workspace()->m_monitor->logicalBox() : t->window()->getWindowIdealBoundingBoxIgnoreReserved();
|
|
||||||
|
|
||||||
switch (dir) {
|
|
||||||
case Math::DIRECTION_UP: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x / 2.0, -1.0}; break;
|
|
||||||
case Math::DIRECTION_DOWN: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x / 2.0, WINDOWIDEALBB.size().y + 1.0}; break;
|
|
||||||
case Math::DIRECTION_LEFT: focalPoint = WINDOWIDEALBB.pos() + Vector2D{-1.0, WINDOWIDEALBB.size().y / 2.0}; break;
|
|
||||||
case Math::DIRECTION_RIGHT: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x + 1.0, WINDOWIDEALBB.size().y / 2.0}; break;
|
|
||||||
default: return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return focalPoint;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,6 @@ namespace Layout {
|
||||||
// optional: predict new window's size
|
// optional: predict new window's size
|
||||||
virtual std::optional<Vector2D> predictSizeForNewTarget();
|
virtual std::optional<Vector2D> predictSizeForNewTarget();
|
||||||
|
|
||||||
// Impl'd here: focal point for dir
|
|
||||||
virtual std::optional<Vector2D> focalPointForDir(SP<ITarget> t, Math::eDirection dir);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
IModeAlgorithm() = default;
|
IModeAlgorithm() = default;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,8 +116,6 @@ void CDefaultFloatingAlgorithm::newTarget(SP<ITarget> target) {
|
||||||
PWINDOW->m_reportedSize = PWINDOW->m_pendingReportedSize;
|
PWINDOW->m_reportedSize = PWINDOW->m_pendingReportedSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTarget(target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDefaultFloatingAlgorithm::movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint) {
|
void CDefaultFloatingAlgorithm::movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint) {
|
||||||
|
|
@ -154,8 +152,6 @@ void CDefaultFloatingAlgorithm::movedTarget(SP<ITarget> target, std::optional<Ve
|
||||||
// put around the current center, fit in workArea
|
// put around the current center, fit in workArea
|
||||||
target->setPositionGlobal(fitBoxInWorkArea(CBox{NEW_POS, LAST_SIZE}, target));
|
target->setPositionGlobal(fitBoxInWorkArea(CBox{NEW_POS, LAST_SIZE}, target));
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTarget(target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CBox CDefaultFloatingAlgorithm::fitBoxInWorkArea(const CBox& box, SP<ITarget> t) {
|
CBox CDefaultFloatingAlgorithm::fitBoxInWorkArea(const CBox& box, SP<ITarget> t) {
|
||||||
|
|
@ -177,7 +173,6 @@ CBox CDefaultFloatingAlgorithm::fitBoxInWorkArea(const CBox& box, SP<ITarget> t)
|
||||||
|
|
||||||
void CDefaultFloatingAlgorithm::removeTarget(SP<ITarget> target) {
|
void CDefaultFloatingAlgorithm::removeTarget(SP<ITarget> target) {
|
||||||
target->rememberFloatingSize(target->position().size());
|
target->rememberFloatingSize(target->position().size());
|
||||||
m_datas.erase(target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDefaultFloatingAlgorithm::resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner) {
|
void CDefaultFloatingAlgorithm::resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner) {
|
||||||
|
|
@ -189,8 +184,6 @@ void CDefaultFloatingAlgorithm::resizeTarget(const Vector2D& Δ, SP<ITarget> tar
|
||||||
|
|
||||||
if (g_layoutManager->dragController()->target() == target)
|
if (g_layoutManager->dragController()->target() == target)
|
||||||
target->warpPositionSize();
|
target->warpPositionSize();
|
||||||
|
|
||||||
updateTarget(target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDefaultFloatingAlgorithm::moveTarget(const Vector2D& Δ, SP<ITarget> target) {
|
void CDefaultFloatingAlgorithm::moveTarget(const Vector2D& Δ, SP<ITarget> target) {
|
||||||
|
|
@ -200,17 +193,12 @@ void CDefaultFloatingAlgorithm::moveTarget(const Vector2D& Δ, SP<ITarget> targe
|
||||||
|
|
||||||
if (g_layoutManager->dragController()->target() == target)
|
if (g_layoutManager->dragController()->target() == target)
|
||||||
target->warpPositionSize();
|
target->warpPositionSize();
|
||||||
|
|
||||||
updateTarget(target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDefaultFloatingAlgorithm::swapTargets(SP<ITarget> a, SP<ITarget> b) {
|
void CDefaultFloatingAlgorithm::swapTargets(SP<ITarget> a, SP<ITarget> b) {
|
||||||
auto posABackup = a->position();
|
auto posABackup = a->position();
|
||||||
a->setPositionGlobal(b->position());
|
a->setPositionGlobal(b->position());
|
||||||
b->setPositionGlobal(posABackup);
|
b->setPositionGlobal(posABackup);
|
||||||
|
|
||||||
updateTarget(a);
|
|
||||||
updateTarget(b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDefaultFloatingAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
void CDefaultFloatingAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
||||||
|
|
@ -228,25 +216,4 @@ void CDefaultFloatingAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDire
|
||||||
}
|
}
|
||||||
|
|
||||||
t->setPositionGlobal(pos);
|
t->setPositionGlobal(pos);
|
||||||
|
|
||||||
updateTarget(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDefaultFloatingAlgorithm::recenter(SP<ITarget> t) {
|
|
||||||
if (!m_datas.contains(t)) {
|
|
||||||
IFloatingAlgorithm::recenter(t);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
t->setPositionGlobal(m_datas.at(t).lastBox);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDefaultFloatingAlgorithm::setTargetGeom(const CBox& geom, SP<ITarget> target) {
|
|
||||||
target->setPositionGlobal(geom);
|
|
||||||
|
|
||||||
updateTarget(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDefaultFloatingAlgorithm::updateTarget(SP<ITarget> t) {
|
|
||||||
m_datas[t] = {.lastBox = t->position()};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
#include "../../FloatingAlgorithm.hpp"
|
#include "../../FloatingAlgorithm.hpp"
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
namespace Layout {
|
namespace Layout {
|
||||||
class CAlgorithm;
|
class CAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
@ -19,22 +17,10 @@ namespace Layout::Floating {
|
||||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||||
virtual void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
virtual void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
||||||
|
|
||||||
virtual void setTargetGeom(const CBox& geom, SP<ITarget> target);
|
|
||||||
|
|
||||||
virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
|
virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
|
||||||
virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||||
|
|
||||||
virtual void recenter(SP<ITarget> t);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CBox fitBoxInWorkArea(const CBox& box, SP<ITarget> t);
|
CBox fitBoxInWorkArea(const CBox& box, SP<ITarget> t);
|
||||||
|
|
||||||
void updateTarget(SP<ITarget>);
|
|
||||||
|
|
||||||
struct SWindowData {
|
|
||||||
CBox lastBox;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<WP<ITarget>, SWindowData> m_datas;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -99,13 +99,11 @@ void CDwindleAlgorithm::addTarget(SP<ITarget> target, bool newTarget) {
|
||||||
if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, ACTIVE_MON))
|
if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, ACTIVE_MON))
|
||||||
OPENINGON = getClosestNode(MOUSECOORDS);
|
OPENINGON = getClosestNode(MOUSECOORDS);
|
||||||
|
|
||||||
} else if (*PUSEACTIVE || m_overrideFocalPoint) {
|
} else if (*PUSEACTIVE) {
|
||||||
const auto ACTIVE_WINDOW = Desktop::focusState()->window();
|
const auto ACTIVE_WINDOW = Desktop::focusState()->window();
|
||||||
|
|
||||||
if (m_overrideFocalPoint)
|
if (!m_overrideFocalPoint && ACTIVE_WINDOW && !ACTIVE_WINDOW->m_isFloating && ACTIVE_WINDOW != target->window() && ACTIVE_WINDOW->m_workspace == PWORKSPACE &&
|
||||||
OPENINGON = getClosestNode(*m_overrideFocalPoint);
|
ACTIVE_WINDOW->m_isMapped)
|
||||||
else if (!m_overrideFocalPoint && ACTIVE_WINDOW && !ACTIVE_WINDOW->m_isFloating && ACTIVE_WINDOW != target->window() && ACTIVE_WINDOW->m_workspace == PWORKSPACE &&
|
|
||||||
ACTIVE_WINDOW->m_isMapped)
|
|
||||||
OPENINGON = getNodeFromWindow(ACTIVE_WINDOW);
|
OPENINGON = getNodeFromWindow(ACTIVE_WINDOW);
|
||||||
|
|
||||||
if (!OPENINGON)
|
if (!OPENINGON)
|
||||||
|
|
@ -184,7 +182,7 @@ void CDwindleAlgorithm::addTarget(SP<ITarget> target, bool newTarget) {
|
||||||
// whether or not the override persists after opening one window
|
// whether or not the override persists after opening one window
|
||||||
if (*PERMANENTDIRECTIONOVERRIDE == 0)
|
if (*PERMANENTDIRECTIONOVERRIDE == 0)
|
||||||
m_overrideDirection = Math::DIRECTION_DEFAULT;
|
m_overrideDirection = Math::DIRECTION_DEFAULT;
|
||||||
} else if (*PSMARTSPLIT == 1 || m_overrideFocalPoint) {
|
} else if (*PSMARTSPLIT == 1) {
|
||||||
const auto PARENT_CENTER = NEWPARENT->box.pos() + NEWPARENT->box.size() / 2;
|
const auto PARENT_CENTER = NEWPARENT->box.pos() + NEWPARENT->box.size() / 2;
|
||||||
const auto PARENT_PROPORTIONS = NEWPARENT->box.h / NEWPARENT->box.w;
|
const auto PARENT_PROPORTIONS = NEWPARENT->box.h / NEWPARENT->box.w;
|
||||||
const auto DELTA = MOUSECOORDS - PARENT_CENTER;
|
const auto DELTA = MOUSECOORDS - PARENT_CENTER;
|
||||||
|
|
@ -216,7 +214,10 @@ void CDwindleAlgorithm::addTarget(SP<ITarget> target, bool newTarget) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (*PFORCESPLIT == 0 || !newTarget) {
|
} else if (*PFORCESPLIT == 0 || !newTarget) {
|
||||||
if ((SIDEBYSIDE && MOUSECOORDS.x < NEWPARENT->box.x + (NEWPARENT->box.w / 2.F)) || (!SIDEBYSIDE && MOUSECOORDS.y < NEWPARENT->box.y + (NEWPARENT->box.h / 2.F))) {
|
if ((SIDEBYSIDE &&
|
||||||
|
VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y + NEWPARENT->box.h)) ||
|
||||||
|
(!SIDEBYSIDE &&
|
||||||
|
VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w, NEWPARENT->box.y + NEWPARENT->box.h / 2.f))) {
|
||||||
// we are hovering over the first node, make PNODE first.
|
// we are hovering over the first node, make PNODE first.
|
||||||
NEWPARENT->children[1] = OPENINGON;
|
NEWPARENT->children[1] = OPENINGON;
|
||||||
NEWPARENT->children[0] = PNODE;
|
NEWPARENT->children[0] = PNODE;
|
||||||
|
|
@ -241,10 +242,11 @@ void CDwindleAlgorithm::addTarget(SP<ITarget> target, bool newTarget) {
|
||||||
|
|
||||||
// and update the previous parent if it exists
|
// and update the previous parent if it exists
|
||||||
if (OPENINGON->pParent) {
|
if (OPENINGON->pParent) {
|
||||||
if (OPENINGON->pParent->children[0] == OPENINGON)
|
if (OPENINGON->pParent->children[0] == OPENINGON) {
|
||||||
OPENINGON->pParent->children[0] = NEWPARENT;
|
OPENINGON->pParent->children[0] = NEWPARENT;
|
||||||
else
|
} else {
|
||||||
OPENINGON->pParent->children[1] = NEWPARENT;
|
OPENINGON->pParent->children[1] = NEWPARENT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the children
|
// Update the children
|
||||||
|
|
@ -549,35 +551,41 @@ std::optional<Vector2D> CDwindleAlgorithm::predictSizeForNewTarget() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDwindleAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
void CDwindleAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
||||||
static auto PMONITORFALLBACK = CConfigValue<Hyprlang::INT>("binds:window_direction_monitor_fallback");
|
|
||||||
|
|
||||||
const auto PNODE = getNodeFromTarget(t);
|
const auto PNODE = getNodeFromTarget(t);
|
||||||
const Vector2D originalPos = t->position().middle();
|
const Vector2D originalPos = t->position().middle();
|
||||||
|
|
||||||
if (!PNODE || !t->window())
|
if (!PNODE || !t->window())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto FOCAL_POINT = focalPointForDir(t, dir);
|
Vector2D focalPoint;
|
||||||
|
|
||||||
const auto PMONITORFOCAL = g_pCompositor->getMonitorFromVector(FOCAL_POINT.value_or(t->position().middle()));
|
const auto WINDOWIDEALBB =
|
||||||
|
t->fullscreenMode() != FSMODE_NONE ? m_parent->space()->workspace()->m_monitor->logicalBox() : t->window()->getWindowIdealBoundingBoxIgnoreReserved();
|
||||||
|
|
||||||
if (PMONITORFOCAL != m_parent->space()->workspace()->m_monitor && !*PMONITORFALLBACK)
|
switch (dir) {
|
||||||
return; // noop
|
case Math::DIRECTION_UP: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x / 2.0, -1.0}; break;
|
||||||
|
case Math::DIRECTION_DOWN: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x / 2.0, WINDOWIDEALBB.size().y + 1.0}; break;
|
||||||
|
case Math::DIRECTION_LEFT: focalPoint = WINDOWIDEALBB.pos() + Vector2D{-1.0, WINDOWIDEALBB.size().y / 2.0}; break;
|
||||||
|
case Math::DIRECTION_RIGHT: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x + 1.0, WINDOWIDEALBB.size().y / 2.0}; break;
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
|
||||||
t->window()->setAnimationsToMove();
|
t->window()->setAnimationsToMove();
|
||||||
|
|
||||||
removeTarget(t);
|
removeTarget(t);
|
||||||
|
|
||||||
|
const auto PMONITORFOCAL = g_pCompositor->getMonitorFromVector(focalPoint);
|
||||||
|
|
||||||
if (PMONITORFOCAL != m_parent->space()->workspace()->m_monitor) {
|
if (PMONITORFOCAL != m_parent->space()->workspace()->m_monitor) {
|
||||||
// move with a focal point
|
// move with a focal point
|
||||||
|
|
||||||
if (PMONITORFOCAL->m_activeWorkspace)
|
if (PMONITORFOCAL->m_activeWorkspace)
|
||||||
t->assignToSpace(PMONITORFOCAL->m_activeWorkspace->m_space, FOCAL_POINT);
|
t->assignToSpace(PMONITORFOCAL->m_activeWorkspace->m_space);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
movedTarget(t, FOCAL_POINT);
|
movedTarget(t, focalPoint);
|
||||||
|
|
||||||
// restore focus to the previous position
|
// restore focus to the previous position
|
||||||
if (silent) {
|
if (silent) {
|
||||||
|
|
@ -657,28 +665,11 @@ std::expected<void, std::string> CDwindleAlgorithm::layoutMsg(const std::string_
|
||||||
const auto CURRENT_NODE = getNodeFromWindow(Desktop::focusState()->window());
|
const auto CURRENT_NODE = getNodeFromWindow(Desktop::focusState()->window());
|
||||||
|
|
||||||
if (ARGS[0] == "togglesplit") {
|
if (ARGS[0] == "togglesplit") {
|
||||||
if (CURRENT_NODE) {
|
if (CURRENT_NODE)
|
||||||
if (!toggleSplit(CURRENT_NODE))
|
toggleSplit(CURRENT_NODE);
|
||||||
return std::unexpected("can't togglesplit in the current workspace");
|
|
||||||
}
|
|
||||||
} else if (ARGS[0] == "swapsplit") {
|
} else if (ARGS[0] == "swapsplit") {
|
||||||
if (CURRENT_NODE) {
|
if (CURRENT_NODE)
|
||||||
if (!swapSplit(CURRENT_NODE))
|
swapSplit(CURRENT_NODE);
|
||||||
return std::unexpected("can't swapsplit in the current workspace");
|
|
||||||
}
|
|
||||||
} else if (ARGS[0] == "rotatesplit") {
|
|
||||||
if (CURRENT_NODE) {
|
|
||||||
int angle = 90;
|
|
||||||
if (!ARGS[1].empty()) {
|
|
||||||
try {
|
|
||||||
angle = std::stoi(std::string{ARGS[1]});
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
Log::logger->log(Log::WARN, "Invalid angle argument for rotatesplit: {}", ARGS[1]);
|
|
||||||
return std::unexpected("Invalid angle argument");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rotateSplit(CURRENT_NODE, angle);
|
|
||||||
}
|
|
||||||
} else if (ARGS[0] == "movetoroot") {
|
} else if (ARGS[0] == "movetoroot") {
|
||||||
auto node = CURRENT_NODE;
|
auto node = CURRENT_NODE;
|
||||||
if (!ARGS[1].empty()) {
|
if (!ARGS[1].empty()) {
|
||||||
|
|
@ -688,8 +679,7 @@ std::expected<void, std::string> CDwindleAlgorithm::layoutMsg(const std::string_
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto STABLE = ARGS[2].empty() || ARGS[2] != "unstable";
|
const auto STABLE = ARGS[2].empty() || ARGS[2] != "unstable";
|
||||||
if (!moveToRoot(node, STABLE))
|
moveToRoot(node, STABLE);
|
||||||
return std::unexpected("can't movetoroot in the current workspace");
|
|
||||||
} else if (ARGS[0] == "preselect") {
|
} else if (ARGS[0] == "preselect") {
|
||||||
auto direction = ARGS[1];
|
auto direction = ARGS[1];
|
||||||
|
|
||||||
|
|
@ -724,102 +714,42 @@ std::expected<void, std::string> CDwindleAlgorithm::layoutMsg(const std::string_
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (ARGS[0] == "splitratio") {
|
|
||||||
auto ratio = ARGS[1];
|
|
||||||
bool exact = ARGS[2].starts_with("exact");
|
|
||||||
|
|
||||||
if (ratio.empty())
|
|
||||||
return std::unexpected("splitratio requires an arg");
|
|
||||||
|
|
||||||
auto delta = getPlusMinusKeywordResult(std::string{ratio}, 0.F);
|
|
||||||
|
|
||||||
if (!CURRENT_NODE || !CURRENT_NODE->pParent)
|
|
||||||
return std::unexpected("cannot alter split ratio on no / single node");
|
|
||||||
|
|
||||||
if (!delta)
|
|
||||||
return std::unexpected(std::format("failed to parse \"{}\" as a delta", ratio));
|
|
||||||
|
|
||||||
const float newRatio = exact ? *delta : CURRENT_NODE->pParent->splitRatio + *delta;
|
|
||||||
CURRENT_NODE->pParent->splitRatio = std::clamp(newRatio, 0.1F, 1.9F);
|
|
||||||
|
|
||||||
CURRENT_NODE->pParent->recalcSizePosRecursive();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CDwindleAlgorithm::toggleSplit(SP<SDwindleNodeData> x) {
|
void CDwindleAlgorithm::toggleSplit(SP<SDwindleNodeData> x) {
|
||||||
if (!x || !x->pParent)
|
if (!x || !x->pParent)
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
if (x->pTarget->fullscreenMode() != FSMODE_NONE)
|
if (x->pTarget->fullscreenMode() != FSMODE_NONE)
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
x->pParent->splitTop = !x->pParent->splitTop;
|
x->pParent->splitTop = !x->pParent->splitTop;
|
||||||
|
|
||||||
x->pParent->recalcSizePosRecursive();
|
x->pParent->recalcSizePosRecursive();
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CDwindleAlgorithm::swapSplit(SP<SDwindleNodeData> x) {
|
void CDwindleAlgorithm::swapSplit(SP<SDwindleNodeData> x) {
|
||||||
if (x->pTarget->fullscreenMode() != FSMODE_NONE || !x->pParent)
|
if (x->pTarget->fullscreenMode() != FSMODE_NONE)
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
std::swap(x->pParent->children[0], x->pParent->children[1]);
|
std::swap(x->pParent->children[0], x->pParent->children[1]);
|
||||||
|
|
||||||
x->pParent->recalcSizePosRecursive();
|
x->pParent->recalcSizePosRecursive();
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDwindleAlgorithm::rotateSplit(SP<SDwindleNodeData> x, int angle) {
|
void CDwindleAlgorithm::moveToRoot(SP<SDwindleNodeData> x, bool stable) {
|
||||||
if (!x || !x->pParent)
|
if (!x || !x->pParent)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (x->pTarget->fullscreenMode() != FSMODE_NONE)
|
if (x->pTarget->fullscreenMode() != FSMODE_NONE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// normalize the angle to multiples of 90 degrees
|
|
||||||
int normalizedAngle = ((sc<int>(angle / 90) % 4) + 4) % 4; // ensures positive modulo
|
|
||||||
|
|
||||||
auto pParent = x->pParent;
|
|
||||||
|
|
||||||
bool shouldSwap = false;
|
|
||||||
|
|
||||||
switch (normalizedAngle) {
|
|
||||||
case 0: // 0 degrees - no change
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (pParent->splitTop)
|
|
||||||
shouldSwap = true;
|
|
||||||
pParent->splitTop = !pParent->splitTop;
|
|
||||||
break;
|
|
||||||
case 2: shouldSwap = true; break;
|
|
||||||
case 3:
|
|
||||||
if (!pParent->splitTop)
|
|
||||||
shouldSwap = true;
|
|
||||||
pParent->splitTop = !pParent->splitTop;
|
|
||||||
break;
|
|
||||||
default: break; // should never happen
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldSwap)
|
|
||||||
std::swap(pParent->children[0], pParent->children[1]);
|
|
||||||
|
|
||||||
pParent->recalcSizePosRecursive();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CDwindleAlgorithm::moveToRoot(SP<SDwindleNodeData> x, bool stable) {
|
|
||||||
if (!x || !x->pParent)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (x->pTarget->fullscreenMode() != FSMODE_NONE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// already at root
|
// already at root
|
||||||
if (!x->pParent->pParent)
|
if (!x->pParent->pParent)
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
auto& pNode = x->pParent->children[0] == x ? x->pParent->children[0] : x->pParent->children[1];
|
auto& pNode = x->pParent->children[0] == x ? x->pParent->children[0] : x->pParent->children[1];
|
||||||
|
|
||||||
|
|
@ -840,6 +770,4 @@ bool CDwindleAlgorithm::moveToRoot(SP<SDwindleNodeData> x, bool stable) {
|
||||||
std::swap(pRoot->children[0], pRoot->children[1]);
|
std::swap(pRoot->children[0], pRoot->children[1]);
|
||||||
|
|
||||||
pRoot->recalcSizePosRecursive();
|
pRoot->recalcSizePosRecursive();
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,10 +48,9 @@ namespace Layout::Tiled {
|
||||||
SP<SDwindleNodeData> getClosestNode(const Vector2D&, SP<ITarget> skip = nullptr);
|
SP<SDwindleNodeData> getClosestNode(const Vector2D&, SP<ITarget> skip = nullptr);
|
||||||
SP<SDwindleNodeData> getMasterNode();
|
SP<SDwindleNodeData> getMasterNode();
|
||||||
|
|
||||||
bool toggleSplit(SP<SDwindleNodeData>);
|
void toggleSplit(SP<SDwindleNodeData>);
|
||||||
bool swapSplit(SP<SDwindleNodeData>);
|
void swapSplit(SP<SDwindleNodeData>);
|
||||||
void rotateSplit(SP<SDwindleNodeData>, int angle = 90);
|
void moveToRoot(SP<SDwindleNodeData>, bool stable = true);
|
||||||
bool moveToRoot(SP<SDwindleNodeData>, bool stable = true);
|
|
||||||
|
|
||||||
Math::eDirection m_overrideDirection = Math::DIRECTION_DEFAULT;
|
Math::eDirection m_overrideDirection = Math::DIRECTION_DEFAULT;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -403,9 +403,7 @@ void CMasterAlgorithm::swapTargets(SP<ITarget> a, SP<ITarget> b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMasterAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
void CMasterAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
||||||
static auto PMONITORFALLBACK = CConfigValue<Hyprlang::INT>("binds:window_direction_monitor_fallback");
|
const auto PWINDOW2 = g_pCompositor->getWindowInDirection(t->window(), dir);
|
||||||
|
|
||||||
const auto PWINDOW2 = g_pCompositor->getWindowInDirection(t->window(), dir);
|
|
||||||
|
|
||||||
if (!t->window())
|
if (!t->window())
|
||||||
return;
|
return;
|
||||||
|
|
@ -426,10 +424,7 @@ void CMasterAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir
|
||||||
t->window()->setAnimationsToMove();
|
t->window()->setAnimationsToMove();
|
||||||
|
|
||||||
if (t->window()->m_workspace != targetWs) {
|
if (t->window()->m_workspace != targetWs) {
|
||||||
if (!*PMONITORFALLBACK)
|
t->assignToSpace(targetWs->m_space);
|
||||||
return; // noop
|
|
||||||
|
|
||||||
t->assignToSpace(targetWs->m_space, focalPointForDir(t, dir));
|
|
||||||
} else if (PWINDOW2) {
|
} else if (PWINDOW2) {
|
||||||
// if same monitor, switch windows
|
// if same monitor, switch windows
|
||||||
g_layoutManager->switchTargets(t, PWINDOW2->layoutTarget());
|
g_layoutManager->switchTargets(t, PWINDOW2->layoutTarget());
|
||||||
|
|
@ -725,8 +720,8 @@ std::expected<void, std::string> CMasterAlgorithm::layoutMsg(const std::string_v
|
||||||
|
|
||||||
for (auto& nd : m_masterNodesData) {
|
for (auto& nd : m_masterNodesData) {
|
||||||
if (!nd->isMaster) {
|
if (!nd->isMaster) {
|
||||||
const auto& newMaster = nd;
|
const auto newMaster = nd;
|
||||||
newMaster->isMaster = true;
|
newMaster->isMaster = true;
|
||||||
|
|
||||||
auto newMasterIt = std::ranges::find(m_masterNodesData, newMaster);
|
auto newMasterIt = std::ranges::find(m_masterNodesData, newMaster);
|
||||||
|
|
||||||
|
|
@ -761,8 +756,8 @@ std::expected<void, std::string> CMasterAlgorithm::layoutMsg(const std::string_v
|
||||||
|
|
||||||
for (auto& nd : m_masterNodesData | std::views::reverse) {
|
for (auto& nd : m_masterNodesData | std::views::reverse) {
|
||||||
if (!nd->isMaster) {
|
if (!nd->isMaster) {
|
||||||
const auto& newMaster = nd;
|
const auto newMaster = nd;
|
||||||
newMaster->isMaster = true;
|
newMaster->isMaster = true;
|
||||||
|
|
||||||
auto newMasterIt = std::ranges::find(m_masterNodesData, newMaster);
|
auto newMasterIt = std::ranges::find(m_masterNodesData, newMaster);
|
||||||
|
|
||||||
|
|
@ -961,9 +956,7 @@ void CMasterAlgorithm::calculateWorkspace() {
|
||||||
const auto STACKWINDOWS = WINDOWS - MASTERS;
|
const auto STACKWINDOWS = WINDOWS - MASTERS;
|
||||||
const auto WORKAREA = m_parent->space()->workArea();
|
const auto WORKAREA = m_parent->space()->workArea();
|
||||||
const auto PMONITOR = m_parent->space()->workspace()->m_monitor;
|
const auto PMONITOR = m_parent->space()->workspace()->m_monitor;
|
||||||
const auto reservedLeft = PMONITOR ? PMONITOR->m_reservedArea.left() : 0;
|
const auto UNRESERVED_WIDTH = WORKAREA.width + PMONITOR->m_reservedArea.left() + PMONITOR->m_reservedArea.right();
|
||||||
const auto reservedRight = PMONITOR ? PMONITOR->m_reservedArea.right() : 0;
|
|
||||||
const auto UNRESERVED_WIDTH = WORKAREA.width + reservedLeft + reservedRight;
|
|
||||||
|
|
||||||
if (orientation == ORIENTATION_CENTER) {
|
if (orientation == ORIENTATION_CENTER) {
|
||||||
if (STACKWINDOWS >= *SLAVECOUNTFORCENTER)
|
if (STACKWINDOWS >= *SLAVECOUNTFORCENTER)
|
||||||
|
|
@ -1081,7 +1074,7 @@ void CMasterAlgorithm::calculateWorkspace() {
|
||||||
}
|
}
|
||||||
|
|
||||||
nd->size = Vector2D(WIDTH, HEIGHT);
|
nd->size = Vector2D(WIDTH, HEIGHT);
|
||||||
nd->position = (*PIGNORERESERVED && centerMasterWindow ? WORKAREA.pos() - Vector2D(reservedLeft, 0.0) : WORKAREA.pos()) + Vector2D(nextX, nextY);
|
nd->position = (*PIGNORERESERVED && centerMasterWindow ? WORKAREA.pos() - Vector2D(PMONITOR->m_reservedArea.left(), 0.0) : WORKAREA.pos()) + Vector2D(nextX, nextY);
|
||||||
nd->pTarget->setPositionGlobal({nd->position, nd->size});
|
nd->pTarget->setPositionGlobal({nd->position, nd->size});
|
||||||
|
|
||||||
mastersLeft--;
|
mastersLeft--;
|
||||||
|
|
@ -1199,7 +1192,7 @@ void CMasterAlgorithm::calculateWorkspace() {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (onRight) {
|
if (onRight) {
|
||||||
nextX = WIDTH + PMASTERNODE->size.x - (*PIGNORERESERVED ? reservedLeft : 0);
|
nextX = WIDTH + PMASTERNODE->size.x - (*PIGNORERESERVED ? PMONITOR->m_reservedArea.left() : 0);
|
||||||
nextY = nextYR;
|
nextY = nextYR;
|
||||||
heightLeft = heightLeftR;
|
heightLeft = heightLeftR;
|
||||||
slavesLeft = slavesLeftR;
|
slavesLeft = slavesLeftR;
|
||||||
|
|
@ -1224,7 +1217,7 @@ void CMasterAlgorithm::calculateWorkspace() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nd->size = Vector2D(*PIGNORERESERVED ? (WIDTH - (onRight ? reservedRight : reservedLeft)) : WIDTH, HEIGHT);
|
nd->size = Vector2D(*PIGNORERESERVED ? (WIDTH - (onRight ? PMONITOR->m_reservedArea.right() : PMONITOR->m_reservedArea.left())) : WIDTH, HEIGHT);
|
||||||
nd->position = WORKAREA.pos() + Vector2D(nextX, nextY);
|
nd->position = WORKAREA.pos() + Vector2D(nextX, nextY);
|
||||||
nd->pTarget->setPositionGlobal({nd->position, nd->size});
|
nd->pTarget->setPositionGlobal({nd->position, nd->size});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -202,11 +202,6 @@ void CMonocleAlgorithm::swapTargets(SP<ITarget> a, SP<ITarget> b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMonocleAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
void CMonocleAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
||||||
static auto PMONITORFALLBACK = CConfigValue<Hyprlang::INT>("binds:window_direction_monitor_fallback");
|
|
||||||
|
|
||||||
if (!*PMONITORFALLBACK)
|
|
||||||
return; // noop
|
|
||||||
|
|
||||||
// try to find a monitor in the specified direction, thats the logical thing
|
// try to find a monitor in the specified direction, thats the logical thing
|
||||||
if (!t || !t->space() || !t->space()->workspace())
|
if (!t || !t->space() || !t->space()->workspace())
|
||||||
return;
|
return;
|
||||||
|
|
@ -220,7 +215,7 @@ void CMonocleAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection di
|
||||||
if (t->window())
|
if (t->window())
|
||||||
t->window()->setAnimationsToMove();
|
t->window()->setAnimationsToMove();
|
||||||
|
|
||||||
t->assignToSpace(TARGETWS->m_space, focalPointForDir(t, dir));
|
t->assignToSpace(TARGETWS->m_space);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,14 +55,12 @@ size_t CScrollTapeController::addStrip(float size) {
|
||||||
return m_strips.size() - 1;
|
return m_strips.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CScrollTapeController::insertStrip(ssize_t afterIndex, float size) {
|
void CScrollTapeController::insertStrip(size_t afterIndex, float size) {
|
||||||
if (afterIndex >= sc<ssize_t>(m_strips.size())) {
|
if (afterIndex >= m_strips.size()) {
|
||||||
addStrip(size);
|
addStrip(size);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
afterIndex = std::clamp(afterIndex, sc<ssize_t>(-1L), sc<ssize_t>(INT32_MAX));
|
|
||||||
|
|
||||||
SStripData newStrip;
|
SStripData newStrip;
|
||||||
newStrip.size = size;
|
newStrip.size = size;
|
||||||
m_strips.insert(m_strips.begin() + afterIndex + 1, newStrip);
|
m_strips.insert(m_strips.begin() + afterIndex + 1, newStrip);
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ namespace Layout::Tiled {
|
||||||
bool isReversed() const;
|
bool isReversed() const;
|
||||||
|
|
||||||
size_t addStrip(float size = 1.0F);
|
size_t addStrip(float size = 1.0F);
|
||||||
void insertStrip(ssize_t afterIndex, float size = 1.0F);
|
void insertStrip(size_t afterIndex, float size = 1.0F);
|
||||||
void removeStrip(size_t index);
|
void removeStrip(size_t index);
|
||||||
size_t stripCount() const;
|
size_t stripCount() const;
|
||||||
SStripData& getStrip(size_t index);
|
SStripData& getStrip(size_t index);
|
||||||
|
|
|
||||||
|
|
@ -190,12 +190,10 @@ size_t SColumnData::idx(SP<ITarget> t) {
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t SColumnData::idxForHeight(float y) {
|
size_t SColumnData::idxForHeight(float y) {
|
||||||
if (targetDatas.empty())
|
|
||||||
return 0;
|
|
||||||
for (size_t i = 0; i < targetDatas.size(); ++i) {
|
for (size_t i = 0; i < targetDatas.size(); ++i) {
|
||||||
if (targetDatas[i]->target->position().y < y)
|
if (targetDatas[i]->target->position().y < y)
|
||||||
continue;
|
continue;
|
||||||
return i == 0 ? 0 : i - 1;
|
return i - 1;
|
||||||
}
|
}
|
||||||
return targetDatas.size() - 1;
|
return targetDatas.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
@ -247,28 +245,24 @@ void SColumnData::remove(SP<ITarget> t) {
|
||||||
scrollingData->remove(self.lock());
|
scrollingData->remove(self.lock());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SColumnData::up(SP<SScrollingTargetData> w) {
|
void SColumnData::up(SP<SScrollingTargetData> w) {
|
||||||
for (size_t i = 1; i < targetDatas.size(); ++i) {
|
for (size_t i = 1; i < targetDatas.size(); ++i) {
|
||||||
if (targetDatas[i] != w)
|
if (targetDatas[i] != w)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::swap(targetDatas[i], targetDatas[i - 1]);
|
std::swap(targetDatas[i], targetDatas[i - 1]);
|
||||||
return true;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SColumnData::down(SP<SScrollingTargetData> w) {
|
void SColumnData::down(SP<SScrollingTargetData> w) {
|
||||||
for (size_t i = 0; i < targetDatas.size() - 1; ++i) {
|
for (size_t i = 0; i < targetDatas.size() - 1; ++i) {
|
||||||
if (targetDatas[i] != w)
|
if (targetDatas[i] != w)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::swap(targetDatas[i], targetDatas[i + 1]);
|
std::swap(targetDatas[i], targetDatas[i + 1]);
|
||||||
return true;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SP<SScrollingTargetData> SColumnData::next(SP<SScrollingTargetData> w) {
|
SP<SScrollingTargetData> SColumnData::next(SP<SScrollingTargetData> w) {
|
||||||
|
|
@ -302,21 +296,23 @@ SScrollingData::SScrollingData(CScrollingAlgorithm* algo) : algorithm(algo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SP<SColumnData> SScrollingData::add() {
|
SP<SColumnData> SScrollingData::add() {
|
||||||
auto col = columns.emplace_back(makeShared<SColumnData>(self.lock()));
|
static const auto PCOLWIDTH = CConfigValue<Hyprlang::FLOAT>("scrolling:column_width");
|
||||||
col->self = col;
|
auto col = columns.emplace_back(makeShared<SColumnData>(self.lock()));
|
||||||
|
col->self = col;
|
||||||
|
|
||||||
size_t stripIdx = controller->addStrip(algorithm->defaultColumnWidth());
|
size_t stripIdx = controller->addStrip(*PCOLWIDTH);
|
||||||
controller->getStrip(stripIdx).userData = col;
|
controller->getStrip(stripIdx).userData = col;
|
||||||
|
|
||||||
return col;
|
return col;
|
||||||
}
|
}
|
||||||
|
|
||||||
SP<SColumnData> SScrollingData::add(int after) {
|
SP<SColumnData> SScrollingData::add(int after) {
|
||||||
auto col = makeShared<SColumnData>(self.lock());
|
static const auto PCOLWIDTH = CConfigValue<Hyprlang::FLOAT>("scrolling:column_width");
|
||||||
col->self = col;
|
auto col = makeShared<SColumnData>(self.lock());
|
||||||
|
col->self = col;
|
||||||
columns.insert(columns.begin() + after + 1, col);
|
columns.insert(columns.begin() + after + 1, col);
|
||||||
|
|
||||||
controller->insertStrip(after, algorithm->defaultColumnWidth());
|
controller->insertStrip(after, *PCOLWIDTH);
|
||||||
controller->getStrip(after + 1).userData = col;
|
controller->getStrip(after + 1).userData = col;
|
||||||
|
|
||||||
return col;
|
return col;
|
||||||
|
|
@ -470,21 +466,6 @@ CScrollingAlgorithm::CScrollingAlgorithm() {
|
||||||
m_scrollingData = makeShared<SScrollingData>(this);
|
m_scrollingData = makeShared<SScrollingData>(this);
|
||||||
m_scrollingData->self = m_scrollingData;
|
m_scrollingData->self = m_scrollingData;
|
||||||
|
|
||||||
// Helper to parse explicit_column_widths string
|
|
||||||
auto parseColumnWidths = [](const std::string& dir) -> std::vector<float> {
|
|
||||||
auto widthVec = std::vector<float>();
|
|
||||||
|
|
||||||
CConstVarList widths(dir, 0, ',');
|
|
||||||
for (auto& w : widths) {
|
|
||||||
try {
|
|
||||||
widthVec.emplace_back(std::clamp(std::stof(std::string{w}), MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH));
|
|
||||||
} catch (...) { Log::logger->log(Log::ERR, "scrolling: Failed to parse width {} as float", w); }
|
|
||||||
}
|
|
||||||
if (widthVec.empty())
|
|
||||||
widthVec = {0.333, 0.5, 0.667, 1.0}; // default
|
|
||||||
return widthVec;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper to parse direction string
|
// Helper to parse direction string
|
||||||
auto parseDirection = [](const std::string& dir) -> eScrollDirection {
|
auto parseDirection = [](const std::string& dir) -> eScrollDirection {
|
||||||
if (dir == "left")
|
if (dir == "left")
|
||||||
|
|
@ -497,11 +478,19 @@ CScrollingAlgorithm::CScrollingAlgorithm() {
|
||||||
return SCROLL_DIR_RIGHT; // default
|
return SCROLL_DIR_RIGHT; // default
|
||||||
};
|
};
|
||||||
|
|
||||||
m_configCallback = Event::bus()->m_events.config.reloaded.listen([this, parseColumnWidths, parseDirection] {
|
m_configCallback = Event::bus()->m_events.config.reloaded.listen([this, parseDirection] {
|
||||||
static const auto PCONFDIRECTION = CConfigValue<Hyprlang::STRING>("scrolling:direction");
|
static const auto PCONFDIRECTION = CConfigValue<Hyprlang::STRING>("scrolling:direction");
|
||||||
|
|
||||||
m_config.configuredWidths.clear();
|
m_config.configuredWidths.clear();
|
||||||
m_config.configuredWidths = parseColumnWidths(*PCONFWIDTHS);
|
|
||||||
|
CConstVarList widths(*PCONFWIDTHS, 0, ',');
|
||||||
|
for (auto& w : widths) {
|
||||||
|
try {
|
||||||
|
m_config.configuredWidths.emplace_back(std::stof(std::string{w}));
|
||||||
|
} catch (...) { Log::logger->log(Log::ERR, "scrolling: Failed to parse width {} as float", w); }
|
||||||
|
}
|
||||||
|
if (m_config.configuredWidths.empty())
|
||||||
|
m_config.configuredWidths = {0.333, 0.5, 0.667, 1.0};
|
||||||
|
|
||||||
// Update scroll direction
|
// Update scroll direction
|
||||||
m_scrollingData->controller->setDirection(parseDirection(*PCONFDIRECTION));
|
m_scrollingData->controller->setDirection(parseDirection(*PCONFDIRECTION));
|
||||||
|
|
@ -534,7 +523,7 @@ CScrollingAlgorithm::CScrollingAlgorithm() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize default widths and direction
|
// Initialize default widths and direction
|
||||||
m_config.configuredWidths = parseColumnWidths(*PCONFWIDTHS);
|
m_config.configuredWidths = {0.333, 0.5, 0.667, 1.0};
|
||||||
m_scrollingData->controller->setDirection(parseDirection(*PCONFDIRECTION));
|
m_scrollingData->controller->setDirection(parseDirection(*PCONFDIRECTION));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -627,20 +616,16 @@ void CScrollingAlgorithm::removeTarget(SP<ITarget> target) {
|
||||||
|
|
||||||
if (!m_scrollingData->next(DATA->column.lock()) && DATA->column->targetDatas.size() <= 1) {
|
if (!m_scrollingData->next(DATA->column.lock()) && DATA->column->targetDatas.size() <= 1) {
|
||||||
// move the view if this is the last column
|
// move the view if this is the last column
|
||||||
const auto USABLE = usableArea();
|
const auto USABLE = usableArea();
|
||||||
const bool isPrimaryHoriz = m_scrollingData->controller->isPrimaryHorizontal();
|
m_scrollingData->controller->adjustOffset(-(USABLE.w * DATA->column->getColumnWidth()));
|
||||||
const double usablePrimary = isPrimaryHoriz ? USABLE.w : USABLE.h;
|
|
||||||
m_scrollingData->controller->adjustOffset(-(usablePrimary * DATA->column->getColumnWidth()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DATA->column->remove(target);
|
DATA->column->remove(target);
|
||||||
|
|
||||||
if (!DATA->column) {
|
if (!DATA->column) {
|
||||||
// column got removed, let's ensure we don't leave any cringe extra space
|
// column got removed, let's ensure we don't leave any cringe extra space
|
||||||
const auto USABLE = usableArea();
|
const auto USABLE = usableArea();
|
||||||
const bool isPrimaryHoriz = m_scrollingData->controller->isPrimaryHorizontal();
|
double newOffset = std::clamp(m_scrollingData->controller->getOffset(), 0.0, std::max(m_scrollingData->maxWidth() - USABLE.w, 1.0));
|
||||||
const double usablePrimary = isPrimaryHoriz ? USABLE.w : USABLE.h;
|
|
||||||
const double newOffset = std::clamp(m_scrollingData->controller->getOffset(), 0.0, std::max(m_scrollingData->maxWidth() - usablePrimary, 1.0));
|
|
||||||
m_scrollingData->controller->setOffset(newOffset);
|
m_scrollingData->controller->setOffset(newOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -848,129 +833,66 @@ void CScrollingAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection
|
||||||
}
|
}
|
||||||
|
|
||||||
void CScrollingAlgorithm::moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
void CScrollingAlgorithm::moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
||||||
static auto PMONITORFALLBACK = CConfigValue<Hyprlang::INT>("binds:window_direction_monitor_fallback");
|
const auto DATA = dataFor(t);
|
||||||
|
|
||||||
const auto DATA = dataFor(t);
|
|
||||||
|
|
||||||
if (!DATA)
|
if (!DATA)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const auto TAPE_DIR = getDynamicDirection();
|
||||||
const auto CURRENT_COL = DATA->column.lock();
|
const auto CURRENT_COL = DATA->column.lock();
|
||||||
const auto current_idx = m_scrollingData->idx(CURRENT_COL);
|
const auto current_idx = m_scrollingData->idx(CURRENT_COL);
|
||||||
|
|
||||||
auto rotateDir = [this](Math::eDirection dir) -> Math::eDirection {
|
if (dir == Math::DIRECTION_LEFT) {
|
||||||
switch (m_scrollingData->controller->getDirection()) {
|
const auto COL = m_scrollingData->prev(DATA->column.lock());
|
||||||
case SCROLL_DIR_RIGHT: return dir;
|
|
||||||
case SCROLL_DIR_LEFT: {
|
|
||||||
if (dir == Math::DIRECTION_LEFT)
|
|
||||||
return Math::DIRECTION_RIGHT;
|
|
||||||
if (dir == Math::DIRECTION_RIGHT)
|
|
||||||
return Math::DIRECTION_LEFT;
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
case SCROLL_DIR_UP: {
|
|
||||||
switch (dir) {
|
|
||||||
case Math::DIRECTION_UP: return Math::DIRECTION_RIGHT;
|
|
||||||
case Math::DIRECTION_DOWN: return Math::DIRECTION_LEFT;
|
|
||||||
case Math::DIRECTION_LEFT: return Math::DIRECTION_DOWN;
|
|
||||||
case Math::DIRECTION_RIGHT: return Math::DIRECTION_UP;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
case SCROLL_DIR_DOWN: {
|
|
||||||
switch (dir) {
|
|
||||||
case Math::DIRECTION_UP: return Math::DIRECTION_LEFT;
|
|
||||||
case Math::DIRECTION_DOWN: return Math::DIRECTION_RIGHT;
|
|
||||||
case Math::DIRECTION_LEFT: return Math::DIRECTION_DOWN;
|
|
||||||
case Math::DIRECTION_RIGHT: return Math::DIRECTION_UP;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dir;
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto ROTATED_DIR = rotateDir(dir);
|
|
||||||
|
|
||||||
auto commenceDir = [&]() -> bool {
|
|
||||||
if (ROTATED_DIR == Math::DIRECTION_LEFT) {
|
|
||||||
const auto COL = m_scrollingData->prev(DATA->column.lock());
|
|
||||||
|
|
||||||
// ignore moves to the origin if we are alone
|
|
||||||
if (!COL && current_idx == 0 && DATA->column->targetDatas.size() == 1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
DATA->column->remove(t);
|
|
||||||
|
|
||||||
if (!COL) {
|
|
||||||
const auto NEWCOL = m_scrollingData->add(-1);
|
|
||||||
NEWCOL->add(DATA);
|
|
||||||
m_scrollingData->centerOrFitCol(NEWCOL);
|
|
||||||
} else {
|
|
||||||
if (COL->targetDatas.size() > 0)
|
|
||||||
COL->add(DATA, COL->idxForHeight(g_pInputManager->getMouseCoordsInternal().y));
|
|
||||||
else
|
|
||||||
COL->add(DATA);
|
|
||||||
m_scrollingData->centerOrFitCol(COL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else if (ROTATED_DIR == Math::DIRECTION_RIGHT) {
|
|
||||||
const auto COL = m_scrollingData->next(DATA->column.lock());
|
|
||||||
|
|
||||||
// ignore move to the right when there is no next column and we're alone
|
|
||||||
if (!COL && current_idx == (int64_t)m_scrollingData->columns.size() - 1 && DATA->column->targetDatas.size() == 1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
DATA->column->remove(t);
|
|
||||||
|
|
||||||
if (!COL) {
|
|
||||||
// make a new one
|
|
||||||
const auto NEWCOL = m_scrollingData->add();
|
|
||||||
NEWCOL->add(DATA);
|
|
||||||
m_scrollingData->centerOrFitCol(NEWCOL);
|
|
||||||
} else {
|
|
||||||
if (COL->targetDatas.size() > 0)
|
|
||||||
COL->add(DATA, COL->idxForHeight(g_pInputManager->getMouseCoordsInternal().y));
|
|
||||||
else
|
|
||||||
COL->add(DATA);
|
|
||||||
m_scrollingData->centerOrFitCol(COL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else if (ROTATED_DIR == Math::DIRECTION_UP)
|
|
||||||
return DATA->column->up(DATA);
|
|
||||||
else if (ROTATED_DIR == Math::DIRECTION_DOWN)
|
|
||||||
return DATA->column->down(DATA);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!commenceDir()) {
|
|
||||||
// dir wasn't commenced, move to a workspace if possible
|
|
||||||
// with the original dir
|
|
||||||
|
|
||||||
if (!*PMONITORFALLBACK)
|
|
||||||
return; // noop
|
|
||||||
|
|
||||||
const auto MONINDIR = g_pCompositor->getMonitorInDirection(m_parent->space()->workspace()->m_monitor.lock(), dir);
|
|
||||||
if (MONINDIR && MONINDIR != m_parent->space()->workspace()->m_monitor && MONINDIR->m_activeWorkspace) {
|
|
||||||
t->assignToSpace(MONINDIR->m_activeWorkspace->m_space, focalPointForDir(t, dir));
|
|
||||||
|
|
||||||
m_scrollingData->recalculate();
|
|
||||||
|
|
||||||
|
// ignore moves to the "origin" when on first column and moving opposite to tape direction
|
||||||
|
if (!COL && current_idx == 0 && (TAPE_DIR == SCROLL_DIR_RIGHT || TAPE_DIR == SCROLL_DIR_DOWN))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
DATA->column->remove(t);
|
||||||
|
|
||||||
|
if (!COL) {
|
||||||
|
const auto NEWCOL = m_scrollingData->add(-1);
|
||||||
|
NEWCOL->add(DATA);
|
||||||
|
m_scrollingData->centerOrFitCol(NEWCOL);
|
||||||
|
} else {
|
||||||
|
if (COL->targetDatas.size() > 0)
|
||||||
|
COL->add(DATA, COL->idxForHeight(g_pInputManager->getMouseCoordsInternal().y));
|
||||||
|
else
|
||||||
|
COL->add(DATA);
|
||||||
|
m_scrollingData->centerOrFitCol(COL);
|
||||||
}
|
}
|
||||||
}
|
} else if (dir == Math::DIRECTION_RIGHT) {
|
||||||
|
const auto COL = m_scrollingData->next(DATA->column.lock());
|
||||||
|
|
||||||
|
// ignore moves to the "origin" when on last column and moving opposite to tape direction
|
||||||
|
if (!COL && current_idx == (int64_t)m_scrollingData->columns.size() - 1 && (TAPE_DIR == SCROLL_DIR_LEFT || TAPE_DIR == SCROLL_DIR_UP))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DATA->column->remove(t);
|
||||||
|
|
||||||
|
if (!COL) {
|
||||||
|
// make a new one
|
||||||
|
const auto NEWCOL = m_scrollingData->add();
|
||||||
|
NEWCOL->add(DATA);
|
||||||
|
m_scrollingData->centerOrFitCol(NEWCOL);
|
||||||
|
} else {
|
||||||
|
if (COL->targetDatas.size() > 0)
|
||||||
|
COL->add(DATA, COL->idxForHeight(g_pInputManager->getMouseCoordsInternal().y));
|
||||||
|
else
|
||||||
|
COL->add(DATA);
|
||||||
|
m_scrollingData->centerOrFitCol(COL);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (dir == Math::DIRECTION_UP)
|
||||||
|
DATA->column->up(DATA);
|
||||||
|
else if (dir == Math::DIRECTION_DOWN)
|
||||||
|
DATA->column->down(DATA);
|
||||||
|
|
||||||
m_scrollingData->recalculate();
|
m_scrollingData->recalculate();
|
||||||
focusTargetUpdate(t);
|
focusTargetUpdate(t);
|
||||||
|
if (t->window())
|
||||||
|
g_pCompositor->warpCursorTo(t->window()->middle());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<void, std::string> CScrollingAlgorithm::layoutMsg(const std::string_view& sv) {
|
std::expected<void, std::string> CScrollingAlgorithm::layoutMsg(const std::string_view& sv) {
|
||||||
|
|
@ -1255,9 +1177,8 @@ std::expected<void, std::string> CScrollingAlgorithm::layoutMsg(const std::strin
|
||||||
m_scrollingData->recalculate();
|
m_scrollingData->recalculate();
|
||||||
}
|
}
|
||||||
} else if (ARGS[0] == "focus") {
|
} else if (ARGS[0] == "focus") {
|
||||||
const auto TDATA = dataFor(Desktop::focusState()->window() ? Desktop::focusState()->window()->layoutTarget() : nullptr);
|
const auto TDATA = dataFor(Desktop::focusState()->window() ? Desktop::focusState()->window()->layoutTarget() : nullptr);
|
||||||
static const auto PNOFALLBACK = CConfigValue<Hyprlang::INT>("general:no_focus_fallback");
|
static const auto PNOFALLBACK = CConfigValue<Hyprlang::INT>("general:no_focus_fallback");
|
||||||
static const auto PCONFWRAPFOCUS = CConfigValue<Hyprlang::INT>("scrolling:wrap_focus");
|
|
||||||
|
|
||||||
if (!TDATA || ARGS[1].empty())
|
if (!TDATA || ARGS[1].empty())
|
||||||
return std::unexpected("no window to focus");
|
return std::unexpected("no window to focus");
|
||||||
|
|
@ -1313,7 +1234,7 @@ std::expected<void, std::string> CScrollingAlgorithm::layoutMsg(const std::strin
|
||||||
g_pCompositor->warpCursorTo(TDATA->target->window()->middle());
|
g_pCompositor->warpCursorTo(TDATA->target->window()->middle());
|
||||||
return {};
|
return {};
|
||||||
} else
|
} else
|
||||||
PREV = (*PCONFWRAPFOCUS == 1) ? m_scrollingData->columns.back() : m_scrollingData->columns.front();
|
PREV = m_scrollingData->columns.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pTargetData = findBestNeighbor(TDATA, PREV);
|
auto pTargetData = findBestNeighbor(TDATA, PREV);
|
||||||
|
|
@ -1335,7 +1256,7 @@ std::expected<void, std::string> CScrollingAlgorithm::layoutMsg(const std::strin
|
||||||
g_pCompositor->warpCursorTo(TDATA->target->window()->middle());
|
g_pCompositor->warpCursorTo(TDATA->target->window()->middle());
|
||||||
return {};
|
return {};
|
||||||
} else
|
} else
|
||||||
NEXT = (*PCONFWRAPFOCUS == 1) ? m_scrollingData->columns.front() : m_scrollingData->columns.back();
|
NEXT = m_scrollingData->columns.front();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pTargetData = findBestNeighbor(TDATA, NEXT);
|
auto pTargetData = findBestNeighbor(TDATA, NEXT);
|
||||||
|
|
@ -1362,8 +1283,6 @@ std::expected<void, std::string> CScrollingAlgorithm::layoutMsg(const std::strin
|
||||||
|
|
||||||
m_scrollingData->recalculate();
|
m_scrollingData->recalculate();
|
||||||
} else if (ARGS[0] == "swapcol") {
|
} else if (ARGS[0] == "swapcol") {
|
||||||
static const auto PCONFWRAPSWAPCOL = CConfigValue<Hyprlang::INT>("scrolling:wrap_swapcol");
|
|
||||||
|
|
||||||
if (ARGS.size() < 2)
|
if (ARGS.size() < 2)
|
||||||
return std::unexpected("not enough args");
|
return std::unexpected("not enough args");
|
||||||
|
|
||||||
|
|
@ -1389,15 +1308,9 @@ std::expected<void, std::string> CScrollingAlgorithm::layoutMsg(const std::strin
|
||||||
|
|
||||||
// wrap around swaps
|
// wrap around swaps
|
||||||
if (direction == "l")
|
if (direction == "l")
|
||||||
if (*PCONFWRAPSWAPCOL == 1)
|
targetIdx = (currentIdx == 0) ? (colCount - 1) : (currentIdx - 1);
|
||||||
targetIdx = (currentIdx == 0) ? (colCount - 1) : (currentIdx - 1);
|
|
||||||
else
|
|
||||||
targetIdx = (currentIdx == 0) ? 0 : (currentIdx - 1);
|
|
||||||
else if (direction == "r")
|
else if (direction == "r")
|
||||||
if (*PCONFWRAPSWAPCOL == 1)
|
targetIdx = (currentIdx == (int64_t)colCount - 1) ? 0 : (currentIdx + 1);
|
||||||
targetIdx = (currentIdx == (int64_t)colCount - 1) ? 0 : (currentIdx + 1);
|
|
||||||
else
|
|
||||||
targetIdx = (currentIdx == (int64_t)colCount - 1) ? (colCount - 1) : (currentIdx + 1);
|
|
||||||
else
|
else
|
||||||
return std::unexpected("no target (invalid direction?)");
|
return std::unexpected("no target (invalid direction?)");
|
||||||
;
|
;
|
||||||
|
|
@ -1505,14 +1418,9 @@ CBox CScrollingAlgorithm::usableArea() {
|
||||||
CBox box = m_parent->space()->workArea();
|
CBox box = m_parent->space()->workArea();
|
||||||
|
|
||||||
// doesn't matter, this happens when this algo is about to be destroyed
|
// doesn't matter, this happens when this algo is about to be destroyed
|
||||||
if (!m_parent->space()->workspace() || !m_parent->space()->workspace()->m_monitor)
|
if (!m_parent->space()->workspace())
|
||||||
return box;
|
return box;
|
||||||
|
|
||||||
box.translate(-m_parent->space()->workspace()->m_monitor->m_position);
|
box.translate(-m_parent->space()->workspace()->m_monitor->m_position);
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
float CScrollingAlgorithm::defaultColumnWidth() {
|
|
||||||
static const auto PCOLWIDTH = CConfigValue<Hyprlang::FLOAT>("scrolling:column_width");
|
|
||||||
return std::clamp(*PCOLWIDTH, MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ namespace Layout::Tiled {
|
||||||
// index of lowest target that is above y.
|
// index of lowest target that is above y.
|
||||||
size_t idxForHeight(float y);
|
size_t idxForHeight(float y);
|
||||||
|
|
||||||
bool up(SP<SScrollingTargetData> w);
|
void up(SP<SScrollingTargetData> w);
|
||||||
bool down(SP<SScrollingTargetData> w);
|
void down(SP<SScrollingTargetData> w);
|
||||||
|
|
||||||
SP<SScrollingTargetData> next(SP<SScrollingTargetData> w);
|
SP<SScrollingTargetData> next(SP<SScrollingTargetData> w);
|
||||||
SP<SScrollingTargetData> prev(SP<SScrollingTargetData> w);
|
SP<SScrollingTargetData> prev(SP<SScrollingTargetData> w);
|
||||||
|
|
@ -138,8 +138,6 @@ namespace Layout::Tiled {
|
||||||
void moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool silent);
|
void moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||||
void focusOnInput(SP<ITarget> target, eInputMode input);
|
void focusOnInput(SP<ITarget> target, eInputMode input);
|
||||||
|
|
||||||
float defaultColumnWidth();
|
|
||||||
|
|
||||||
friend struct SScrollingData;
|
friend struct SScrollingData;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
#include "../../debug/log/Logger.hpp"
|
#include "../../debug/log/Logger.hpp"
|
||||||
#include "../../desktop/Workspace.hpp"
|
#include "../../desktop/Workspace.hpp"
|
||||||
#include "../../config/ConfigManager.hpp"
|
#include "../../config/ConfigManager.hpp"
|
||||||
#include "../../event/EventBus.hpp"
|
|
||||||
|
|
||||||
using namespace Layout;
|
using namespace Layout;
|
||||||
|
|
||||||
|
|
@ -18,12 +17,6 @@ SP<CSpace> CSpace::create(PHLWORKSPACE w) {
|
||||||
|
|
||||||
CSpace::CSpace(PHLWORKSPACE parent) : m_parent(parent) {
|
CSpace::CSpace(PHLWORKSPACE parent) : m_parent(parent) {
|
||||||
recheckWorkArea();
|
recheckWorkArea();
|
||||||
|
|
||||||
// NOLINTNEXTLINE
|
|
||||||
m_geomUpdateCallback = Event::bus()->m_events.monitor.layoutChanged.listen([this] {
|
|
||||||
recheckWorkArea();
|
|
||||||
m_algorithm->recalculate();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSpace::add(SP<ITarget> t) {
|
void CSpace::add(SP<ITarget> t) {
|
||||||
|
|
@ -183,11 +176,6 @@ void CSpace::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool sil
|
||||||
m_algorithm->moveTargetInDirection(t, dir, silent);
|
m_algorithm->moveTargetInDirection(t, dir, silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSpace::setTargetGeom(const CBox& box, SP<ITarget> target) {
|
|
||||||
if (m_algorithm)
|
|
||||||
m_algorithm->setTargetGeom(box, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
SP<ITarget> CSpace::getNextCandidate(SP<ITarget> old) {
|
SP<ITarget> CSpace::getNextCandidate(SP<ITarget> old) {
|
||||||
return !m_algorithm ? nullptr : m_algorithm->getNextCandidate(old);
|
return !m_algorithm ? nullptr : m_algorithm->getNextCandidate(old);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@ namespace Layout {
|
||||||
|
|
||||||
void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||||
void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
||||||
void setTargetGeom(const CBox& box, SP<ITarget> target); // only for float
|
|
||||||
|
|
||||||
SP<CAlgorithm> algorithm() const;
|
SP<CAlgorithm> algorithm() const;
|
||||||
|
|
||||||
|
|
@ -64,8 +63,5 @@ namespace Layout {
|
||||||
|
|
||||||
// work area is in global coords
|
// work area is in global coords
|
||||||
CBox m_workArea, m_floatingWorkArea;
|
CBox m_workArea, m_floatingWorkArea;
|
||||||
|
|
||||||
// for recalc
|
|
||||||
CHyprSignalListener m_geomUpdateCallback;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -239,8 +239,6 @@ void CDragStateController::dragEnd() {
|
||||||
|
|
||||||
draggingTarget->damageEntire();
|
draggingTarget->damageEntire();
|
||||||
|
|
||||||
g_layoutManager->setTargetGeom(draggingTarget->position(), draggingTarget);
|
|
||||||
|
|
||||||
Desktop::focusState()->fullWindowFocus(draggingTarget->window(), Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
Desktop::focusState()->fullWindowFocus(draggingTarget->window(), Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||||
|
|
||||||
m_wasDraggingWindow = false;
|
m_wasDraggingWindow = false;
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,6 @@
|
||||||
#include "../../Compositor.hpp"
|
#include "../../Compositor.hpp"
|
||||||
#include "../../render/Renderer.hpp"
|
#include "../../render/Renderer.hpp"
|
||||||
|
|
||||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
|
||||||
|
|
||||||
using namespace Hyprutils::Utils;
|
|
||||||
using namespace Layout;
|
using namespace Layout;
|
||||||
|
|
||||||
SP<ITarget> CWindowTarget::create(PHLWINDOW w) {
|
SP<ITarget> CWindowTarget::create(PHLWINDOW w) {
|
||||||
|
|
@ -37,9 +34,6 @@ void CWindowTarget::setPositionGlobal(const CBox& box) {
|
||||||
|
|
||||||
void CWindowTarget::updatePos() {
|
void CWindowTarget::updatePos() {
|
||||||
|
|
||||||
g_pHyprRenderer->damageWindow(m_window.lock());
|
|
||||||
CScopeGuard x([this] { g_pHyprRenderer->damageWindow(m_window.lock()); });
|
|
||||||
|
|
||||||
if (!m_space)
|
if (!m_space)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -61,11 +55,6 @@ void CWindowTarget::updatePos() {
|
||||||
|
|
||||||
// Tiled is more complicated.
|
// Tiled is more complicated.
|
||||||
|
|
||||||
// if we are in maximized, force the box to be max work area.
|
|
||||||
// TODO: this shouldn't be here.
|
|
||||||
if (fullscreenMode() == FSMODE_MAXIMIZED)
|
|
||||||
ITarget::setPositionGlobal(m_space->workArea(floating()));
|
|
||||||
|
|
||||||
const auto PMONITOR = m_space->workspace()->m_monitor;
|
const auto PMONITOR = m_space->workspace()->m_monitor;
|
||||||
const auto PWORKSPACE = m_space->workspace();
|
const auto PWORKSPACE = m_space->workspace();
|
||||||
|
|
||||||
|
|
@ -115,7 +104,7 @@ void CWindowTarget::updatePos() {
|
||||||
|
|
||||||
Vector2D ratioPadding;
|
Vector2D ratioPadding;
|
||||||
|
|
||||||
if ((*REQUESTEDRATIO).y != 0 && m_space->algorithm()->tiledTargets() <= 1 && fullscreenMode() == FSMODE_NONE) {
|
if ((*REQUESTEDRATIO).y != 0 && m_space->algorithm()->tiledTargets() <= 1) {
|
||||||
const Vector2D originalSize = MONITOR_WORKAREA.size();
|
const Vector2D originalSize = MONITOR_WORKAREA.size();
|
||||||
|
|
||||||
const double requestedRatio = (*REQUESTEDRATIO).x / (*REQUESTEDRATIO).y;
|
const double requestedRatio = (*REQUESTEDRATIO).x / (*REQUESTEDRATIO).y;
|
||||||
|
|
@ -142,7 +131,7 @@ void CWindowTarget::updatePos() {
|
||||||
calcPos = calcPos + GAPOFFSETTOPLEFT + ratioPadding / 2;
|
calcPos = calcPos + GAPOFFSETTOPLEFT + ratioPadding / 2;
|
||||||
calcSize = calcSize - GAPOFFSETTOPLEFT - GAPOFFSETBOTTOMRIGHT - ratioPadding;
|
calcSize = calcSize - GAPOFFSETTOPLEFT - GAPOFFSETBOTTOMRIGHT - ratioPadding;
|
||||||
|
|
||||||
if (isPseudo() && fullscreenMode() == FSMODE_NONE) {
|
if (isPseudo()) {
|
||||||
// Calculate pseudo
|
// Calculate pseudo
|
||||||
float scale = 1;
|
float scale = 1;
|
||||||
|
|
||||||
|
|
@ -173,14 +162,18 @@ void CWindowTarget::updatePos() {
|
||||||
static auto PCLAMP_TILED = CConfigValue<Hyprlang::INT>("misc:size_limits_tiled");
|
static auto PCLAMP_TILED = CConfigValue<Hyprlang::INT>("misc:size_limits_tiled");
|
||||||
|
|
||||||
if (*PCLAMP_TILED) {
|
if (*PCLAMP_TILED) {
|
||||||
Vector2D minSize = m_window->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE});
|
const auto borderSize = m_window->getRealBorderSize();
|
||||||
Vector2D maxSize = m_window->isFullscreen() ? Vector2D{INFINITY, INFINITY} : m_window->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY});
|
Vector2D monitorAvailable = MONITOR_WORKAREA.size() - Vector2D{2.0 * borderSize, 2.0 * borderSize};
|
||||||
calcSize = calcSize.clamp(minSize, maxSize);
|
|
||||||
|
Vector2D minSize = m_window->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable);
|
||||||
|
Vector2D maxSize = m_window->isFullscreen() ? Vector2D{INFINITY, INFINITY} :
|
||||||
|
m_window->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable);
|
||||||
|
calcSize = calcSize.clamp(minSize, maxSize);
|
||||||
|
|
||||||
calcPos += (availableSpace - calcSize) / 2.0;
|
calcPos += (availableSpace - calcSize) / 2.0;
|
||||||
|
|
||||||
calcPos.x = std::clamp(calcPos.x, MONITOR_WORKAREA.x, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w - calcSize.x);
|
calcPos.x = std::clamp(calcPos.x, MONITOR_WORKAREA.x + borderSize, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w - calcSize.x - borderSize);
|
||||||
calcPos.y = std::clamp(calcPos.y, MONITOR_WORKAREA.y, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h - calcSize.y);
|
calcPos.y = std::clamp(calcPos.y, MONITOR_WORKAREA.y + borderSize, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h - calcSize.y - borderSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_window->onSpecialWorkspace() && !m_window->isFullscreen()) {
|
if (m_window->onSpecialWorkspace() && !m_window->isFullscreen()) {
|
||||||
|
|
@ -377,6 +370,5 @@ void CWindowTarget::onUpdateSpace() {
|
||||||
m_window->m_monitor = space()->workspace()->m_monitor;
|
m_window->m_monitor = space()->workspace()->m_monitor;
|
||||||
m_window->moveToWorkspace(space()->workspace());
|
m_window->moveToWorkspace(space()->workspace());
|
||||||
m_window->updateToplevel();
|
m_window->updateToplevel();
|
||||||
m_window->updateWindowData();
|
|
||||||
m_window->updateWindowDecos();
|
m_window->updateWindowDecos();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,9 @@ CKeybindManager::CKeybindManager() {
|
||||||
m_dispatchers["togglegroup"] = toggleGroup;
|
m_dispatchers["togglegroup"] = toggleGroup;
|
||||||
m_dispatchers["changegroupactive"] = changeGroupActive;
|
m_dispatchers["changegroupactive"] = changeGroupActive;
|
||||||
m_dispatchers["movegroupwindow"] = moveGroupWindow;
|
m_dispatchers["movegroupwindow"] = moveGroupWindow;
|
||||||
|
m_dispatchers["togglesplit"] = toggleSplit;
|
||||||
|
m_dispatchers["swapsplit"] = swapSplit;
|
||||||
|
m_dispatchers["splitratio"] = alterSplitRatio;
|
||||||
m_dispatchers["focusmonitor"] = focusMonitor;
|
m_dispatchers["focusmonitor"] = focusMonitor;
|
||||||
m_dispatchers["movecursortocorner"] = moveCursorToCorner;
|
m_dispatchers["movecursortocorner"] = moveCursorToCorner;
|
||||||
m_dispatchers["movecursor"] = moveCursor;
|
m_dispatchers["movecursor"] = moveCursor;
|
||||||
|
|
@ -1465,10 +1468,9 @@ SDispatchResult CKeybindManager::moveActiveToWorkspaceSilent(std::string args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SDispatchResult CKeybindManager::moveFocusTo(std::string args) {
|
SDispatchResult CKeybindManager::moveFocusTo(std::string args) {
|
||||||
static auto PFULLCYCLE = CConfigValue<Hyprlang::INT>("binds:movefocus_cycles_fullscreen");
|
static auto PFULLCYCLE = CConfigValue<Hyprlang::INT>("binds:movefocus_cycles_fullscreen");
|
||||||
static auto PGROUPCYCLE = CConfigValue<Hyprlang::INT>("binds:movefocus_cycles_groupfirst");
|
static auto PGROUPCYCLE = CConfigValue<Hyprlang::INT>("binds:movefocus_cycles_groupfirst");
|
||||||
static auto PMONITORFALLBACK = CConfigValue<Hyprlang::INT>("binds:window_direction_monitor_fallback");
|
Math::eDirection dir = Math::fromChar(args[0]);
|
||||||
Math::eDirection dir = Math::fromChar(args[0]);
|
|
||||||
|
|
||||||
if (dir == Math::DIRECTION_DEFAULT) {
|
if (dir == Math::DIRECTION_DEFAULT) {
|
||||||
Log::logger->log(Log::ERR, "Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0]);
|
Log::logger->log(Log::ERR, "Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0]);
|
||||||
|
|
@ -1477,8 +1479,7 @@ SDispatchResult CKeybindManager::moveFocusTo(std::string args) {
|
||||||
|
|
||||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||||
if (!PLASTWINDOW || !PLASTWINDOW->aliveAndVisible()) {
|
if (!PLASTWINDOW || !PLASTWINDOW->aliveAndVisible()) {
|
||||||
if (*PMONITORFALLBACK)
|
tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir));
|
||||||
tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir));
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1508,7 +1509,7 @@ SDispatchResult CKeybindManager::moveFocusTo(std::string args) {
|
||||||
|
|
||||||
Log::logger->log(Log::DEBUG, "No window found in direction {}, looking for a monitor", Math::toString(dir));
|
Log::logger->log(Log::DEBUG, "No window found in direction {}, looking for a monitor", Math::toString(dir));
|
||||||
|
|
||||||
if (*PMONITORFALLBACK && tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir)))
|
if (tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir)))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
static auto PNOFALLBACK = CConfigValue<Hyprlang::INT>("general:no_focus_fallback");
|
static auto PNOFALLBACK = CConfigValue<Hyprlang::INT>("general:no_focus_fallback");
|
||||||
|
|
@ -1689,10 +1690,7 @@ SDispatchResult CKeybindManager::changeGroupActive(std::string args) {
|
||||||
// index starts from '1'; '0' means last window
|
// index starts from '1'; '0' means last window
|
||||||
try {
|
try {
|
||||||
const int INDEX = std::stoi(args);
|
const int INDEX = std::stoi(args);
|
||||||
if (INDEX <= 0)
|
PWINDOW->m_group->setCurrent(INDEX);
|
||||||
PWINDOW->m_group->setCurrent(PWINDOW->m_group->size() - 1);
|
|
||||||
else
|
|
||||||
PWINDOW->m_group->setCurrent(INDEX - 1);
|
|
||||||
} catch (...) { return {.success = false, .error = "invalid idx"}; }
|
} catch (...) { return {.success = false, .error = "invalid idx"}; }
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|
@ -1706,6 +1704,18 @@ SDispatchResult CKeybindManager::changeGroupActive(std::string args) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDispatchResult CKeybindManager::toggleSplit(std::string args) {
|
||||||
|
return {.success = false, .error = "removed - use layoutmsg"};
|
||||||
|
}
|
||||||
|
|
||||||
|
SDispatchResult CKeybindManager::swapSplit(std::string args) {
|
||||||
|
return {.success = false, .error = "removed - use layoutmsg"};
|
||||||
|
}
|
||||||
|
|
||||||
|
SDispatchResult CKeybindManager::alterSplitRatio(std::string args) {
|
||||||
|
return {.success = false, .error = "removed - use layoutmsg"};
|
||||||
|
}
|
||||||
|
|
||||||
SDispatchResult CKeybindManager::focusMonitor(std::string arg) {
|
SDispatchResult CKeybindManager::focusMonitor(std::string arg) {
|
||||||
const auto PMONITOR = g_pCompositor->getMonitorFromString(arg);
|
const auto PMONITOR = g_pCompositor->getMonitorFromString(arg);
|
||||||
tryMoveFocusToMonitor(PMONITOR);
|
tryMoveFocusToMonitor(PMONITOR);
|
||||||
|
|
@ -2737,9 +2747,7 @@ void CKeybindManager::moveWindowOutOfGroup(PHLWINDOW pWindow, const std::string&
|
||||||
|
|
||||||
WP<Desktop::View::CGroup> group = pWindow->m_group;
|
WP<Desktop::View::CGroup> group = pWindow->m_group;
|
||||||
|
|
||||||
const auto direction = !dir.empty() ? Math::fromChar(dir[0]) : Math::DIRECTION_DEFAULT;
|
pWindow->m_group->remove(pWindow);
|
||||||
|
|
||||||
pWindow->m_group->remove(pWindow, direction);
|
|
||||||
|
|
||||||
if (*BFOCUSREMOVEDWINDOW || !group) {
|
if (*BFOCUSREMOVEDWINDOW || !group) {
|
||||||
Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND);
|
Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND);
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,10 @@ class CKeybindManager {
|
||||||
static SDispatchResult swapActive(std::string);
|
static SDispatchResult swapActive(std::string);
|
||||||
static SDispatchResult toggleGroup(std::string);
|
static SDispatchResult toggleGroup(std::string);
|
||||||
static SDispatchResult changeGroupActive(std::string);
|
static SDispatchResult changeGroupActive(std::string);
|
||||||
|
static SDispatchResult alterSplitRatio(std::string);
|
||||||
static SDispatchResult focusMonitor(std::string);
|
static SDispatchResult focusMonitor(std::string);
|
||||||
|
static SDispatchResult toggleSplit(std::string);
|
||||||
|
static SDispatchResult swapSplit(std::string);
|
||||||
static SDispatchResult moveCursorToCorner(std::string);
|
static SDispatchResult moveCursorToCorner(std::string);
|
||||||
static SDispatchResult moveCursor(std::string);
|
static SDispatchResult moveCursor(std::string);
|
||||||
static SDispatchResult workspaceOpt(std::string);
|
static SDispatchResult workspaceOpt(std::string);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
#include "../protocols/IdleNotify.hpp"
|
#include "../protocols/IdleNotify.hpp"
|
||||||
#include "../protocols/core/Compositor.hpp"
|
#include "../protocols/core/Compositor.hpp"
|
||||||
#include "../protocols/core/Seat.hpp"
|
#include "../protocols/core/Seat.hpp"
|
||||||
#include "debug/log/Logger.hpp"
|
|
||||||
#include "eventLoop/EventLoopManager.hpp"
|
#include "eventLoop/EventLoopManager.hpp"
|
||||||
#include "../render/pass/TexPassElement.hpp"
|
#include "../render/pass/TexPassElement.hpp"
|
||||||
#include "../managers/input/InputManager.hpp"
|
#include "../managers/input/InputManager.hpp"
|
||||||
|
|
@ -19,12 +18,9 @@
|
||||||
#include "../helpers/time/Time.hpp"
|
#include "../helpers/time/Time.hpp"
|
||||||
#include "../helpers/Drm.hpp"
|
#include "../helpers/Drm.hpp"
|
||||||
#include "../event/EventBus.hpp"
|
#include "../event/EventBus.hpp"
|
||||||
#include <climits>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <gbm.h>
|
#include <gbm.h>
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
#include <hyprutils/math/Region.hpp>
|
|
||||||
#include <hyprutils/math/Vector2D.hpp>
|
|
||||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||||
|
|
||||||
using namespace Hyprutils::Utils;
|
using namespace Hyprutils::Utils;
|
||||||
|
|
@ -410,7 +406,7 @@ bool CPointerManager::setHWCursorBuffer(SP<SMonitorPointerState> state, SP<Aquam
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager::SMonitorPointerState> state, SP<ITexture> texture) {
|
SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager::SMonitorPointerState> state, SP<CTexture> texture) {
|
||||||
auto maxSize = state->monitor->m_output->cursorPlaneSize();
|
auto maxSize = state->monitor->m_output->cursorPlaneSize();
|
||||||
auto const& cursorSize = m_currentCursorImage.size;
|
auto const& cursorSize = m_currentCursorImage.size;
|
||||||
|
|
||||||
|
|
@ -543,23 +539,24 @@ SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager
|
||||||
|
|
||||||
// we need to scale the cursor to the right size, because it might not be (esp with XCursor)
|
// we need to scale the cursor to the right size, because it might not be (esp with XCursor)
|
||||||
const auto SCALE = texture->m_size / (m_currentCursorImage.size / m_currentCursorImage.scale * state->monitor->m_scale);
|
const auto SCALE = texture->m_size / (m_currentCursorImage.size / m_currentCursorImage.scale * state->monitor->m_scale);
|
||||||
const auto SX = SCALE.x, SY = SCALE.y;
|
cairo_matrix_scale(&matrixPre, SCALE.x, SCALE.y);
|
||||||
const auto BW = sc<double>(DMABUF.size.x), BH = sc<double>(DMABUF.size.y);
|
|
||||||
|
|
||||||
// Cairo pattern matrix maps destination coords to source coords (inverse of visual transform).
|
if (TR) {
|
||||||
// x_src = xx * x_dst + xy * y_dst + x0
|
cairo_matrix_rotate(&matrixPre, M_PI_2 * sc<double>(TR));
|
||||||
// y_src = yx * x_dst + yy * y_dst + y0
|
|
||||||
// cairo_matrix_init(&m, xx, yx, xy, yy, x0, y0)
|
// FIXME: this is wrong, and doesn't work for 5, 6 and 7. (flipped + rot)
|
||||||
switch (TR) {
|
// cba to do it rn, does anyone fucking use that??
|
||||||
case WL_OUTPUT_TRANSFORM_NORMAL:
|
if (TR >= WL_OUTPUT_TRANSFORM_FLIPPED) {
|
||||||
default: cairo_matrix_init(&matrixPre, SX, 0, 0, SY, 0, 0); break;
|
cairo_matrix_scale(&matrixPre, -1, 1);
|
||||||
case WL_OUTPUT_TRANSFORM_90: cairo_matrix_init(&matrixPre, 0, SY, -SX, 0, SX * BW, 0); break;
|
cairo_matrix_translate(&matrixPre, -DMABUF.size.x, 0);
|
||||||
case WL_OUTPUT_TRANSFORM_180: cairo_matrix_init(&matrixPre, -SX, 0, 0, -SY, SX * BW, SY * BH); break;
|
}
|
||||||
case WL_OUTPUT_TRANSFORM_270: cairo_matrix_init(&matrixPre, 0, -SY, SX, 0, 0, SY * BH); break;
|
|
||||||
case WL_OUTPUT_TRANSFORM_FLIPPED: cairo_matrix_init(&matrixPre, -SX, 0, 0, SY, SX * BW, 0); break;
|
if (TR == 3 || TR == 7)
|
||||||
case WL_OUTPUT_TRANSFORM_FLIPPED_90: cairo_matrix_init(&matrixPre, 0, SY, SX, 0, 0, 0); break;
|
cairo_matrix_translate(&matrixPre, -DMABUF.size.x, 0);
|
||||||
case WL_OUTPUT_TRANSFORM_FLIPPED_180: cairo_matrix_init(&matrixPre, SX, 0, 0, -SY, 0, SY * BH); break;
|
else if (TR == 2 || TR == 6)
|
||||||
case WL_OUTPUT_TRANSFORM_FLIPPED_270: cairo_matrix_init(&matrixPre, 0, -SY, -SX, 0, SX * BW, SY * BH); break;
|
cairo_matrix_translate(&matrixPre, -DMABUF.size.x, -DMABUF.size.y);
|
||||||
|
else if (TR == 1 || TR == 5)
|
||||||
|
cairo_matrix_translate(&matrixPre, 0, -DMABUF.size.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
cairo_pattern_set_matrix(PATTERNPRE, &matrixPre);
|
cairo_pattern_set_matrix(PATTERNPRE, &matrixPre);
|
||||||
|
|
@ -592,14 +589,15 @@ SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager
|
||||||
|
|
||||||
RBO->bind();
|
RBO->bind();
|
||||||
|
|
||||||
g_pHyprOpenGL->beginSimple(state->monitor.lock(), {0, 0, INT_MAX, INT_MAX}, RBO);
|
const auto& damageSize = state->monitor->m_output->cursorPlaneSize();
|
||||||
|
g_pHyprOpenGL->beginSimple(state->monitor.lock(), {0, 0, damageSize.x, damageSize.y}, RBO);
|
||||||
g_pHyprOpenGL->clear(CHyprColor{0.F, 0.F, 0.F, 0.F}); // ensure the RBO is zero initialized.
|
g_pHyprOpenGL->clear(CHyprColor{0.F, 0.F, 0.F, 0.F}); // ensure the RBO is zero initialized.
|
||||||
|
|
||||||
CBox xbox = {{}, Vector2D{m_currentCursorImage.size / m_currentCursorImage.scale * state->monitor->m_scale}.round()};
|
CBox xbox = {{}, Vector2D{m_currentCursorImage.size / m_currentCursorImage.scale * state->monitor->m_scale}.round()};
|
||||||
Log::logger->log(Log::TRACE, "[pointer] monitor: {}, size: {}, hw buf: {}, scale: {:.2f}, monscale: {:.2f}, xbox: {}", state->monitor->m_name, m_currentCursorImage.size,
|
Log::logger->log(Log::TRACE, "[pointer] monitor: {}, size: {}, hw buf: {}, scale: {:.2f}, monscale: {:.2f}, xbox: {}", state->monitor->m_name, m_currentCursorImage.size,
|
||||||
cursorSize, m_currentCursorImage.scale, state->monitor->m_scale, xbox.size());
|
cursorSize, m_currentCursorImage.scale, state->monitor->m_scale, xbox.size());
|
||||||
|
|
||||||
g_pHyprOpenGL->renderTexture(texture, xbox, {.noCM = true});
|
g_pHyprOpenGL->renderTexture(texture, xbox, {});
|
||||||
|
|
||||||
g_pHyprOpenGL->end();
|
g_pHyprOpenGL->end();
|
||||||
g_pHyprOpenGL->m_renderData.pMonitor.reset();
|
g_pHyprOpenGL->m_renderData.pMonitor.reset();
|
||||||
|
|
@ -903,13 +901,13 @@ const CPointerManager::SCursorImage& CPointerManager::currentCursorImage() {
|
||||||
return m_currentCursorImage;
|
return m_currentCursorImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
SP<ITexture> CPointerManager::getCurrentCursorTexture() {
|
SP<CTexture> CPointerManager::getCurrentCursorTexture() {
|
||||||
if (!m_currentCursorImage.pBuffer && (!m_currentCursorImage.surface || !m_currentCursorImage.surface->resource()->m_current.texture))
|
if (!m_currentCursorImage.pBuffer && (!m_currentCursorImage.surface || !m_currentCursorImage.surface->resource()->m_current.texture))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (m_currentCursorImage.pBuffer) {
|
if (m_currentCursorImage.pBuffer) {
|
||||||
if (!m_currentCursorImage.bufferTex)
|
if (!m_currentCursorImage.bufferTex)
|
||||||
m_currentCursorImage.bufferTex = g_pHyprRenderer->createTexture(m_currentCursorImage.pBuffer, true);
|
m_currentCursorImage.bufferTex = makeShared<CTexture>(m_currentCursorImage.pBuffer, true);
|
||||||
return m_currentCursorImage.bufferTex;
|
return m_currentCursorImage.bufferTex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
class CMonitor;
|
class CMonitor;
|
||||||
class IHID;
|
class IHID;
|
||||||
class ITexture;
|
class CTexture;
|
||||||
|
|
||||||
AQUAMARINE_FORWARD(IBuffer);
|
AQUAMARINE_FORWARD(IBuffer);
|
||||||
|
|
||||||
|
|
@ -71,7 +71,7 @@ class CPointerManager {
|
||||||
|
|
||||||
struct SCursorImage {
|
struct SCursorImage {
|
||||||
SP<Aquamarine::IBuffer> pBuffer;
|
SP<Aquamarine::IBuffer> pBuffer;
|
||||||
SP<ITexture> bufferTex;
|
SP<CTexture> bufferTex;
|
||||||
WP<Desktop::View::CWLSurface> surface;
|
WP<Desktop::View::CWLSurface> surface;
|
||||||
|
|
||||||
Vector2D hotspot;
|
Vector2D hotspot;
|
||||||
|
|
@ -83,7 +83,7 @@ class CPointerManager {
|
||||||
};
|
};
|
||||||
|
|
||||||
const SCursorImage& currentCursorImage();
|
const SCursorImage& currentCursorImage();
|
||||||
SP<ITexture> getCurrentCursorTexture();
|
SP<CTexture> getCurrentCursorTexture();
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
CSignalT<> cursorChanged;
|
CSignalT<> cursorChanged;
|
||||||
|
|
@ -181,7 +181,7 @@ class CPointerManager {
|
||||||
std::vector<SP<SMonitorPointerState>> m_monitorStates;
|
std::vector<SP<SMonitorPointerState>> m_monitorStates;
|
||||||
SP<SMonitorPointerState> stateFor(PHLMONITOR mon);
|
SP<SMonitorPointerState> stateFor(PHLMONITOR mon);
|
||||||
bool attemptHardwareCursor(SP<SMonitorPointerState> state);
|
bool attemptHardwareCursor(SP<SMonitorPointerState> state);
|
||||||
SP<Aquamarine::IBuffer> renderHWCursorBuffer(SP<SMonitorPointerState> state, SP<ITexture> texture);
|
SP<Aquamarine::IBuffer> renderHWCursorBuffer(SP<SMonitorPointerState> state, SP<CTexture> texture);
|
||||||
bool setHWCursorBuffer(SP<SMonitorPointerState> state, SP<Aquamarine::IBuffer> buf);
|
bool setHWCursorBuffer(SP<SMonitorPointerState> state, SP<Aquamarine::IBuffer> buf);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
|
||||||
|
|
@ -847,9 +847,6 @@ void CInputManager::processMouseDownKill(const IPointer::SButtonEvent& e) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_pEventManager->postEvent(SHyprIPCEvent({.event = "kill", .data = std::format("{:x}", rc<uintptr_t>(PWINDOW.m_data))}));
|
|
||||||
Event::bus()->m_events.window.kill.emit(PWINDOW);
|
|
||||||
|
|
||||||
// kill the mf
|
// kill the mf
|
||||||
kill(PWINDOW->getPID(), SIGKILL);
|
kill(PWINDOW->getPID(), SIGKILL);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -169,10 +169,10 @@ bool CCursorshareSession::copy() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto outFB = g_pHyprRenderer->createFB();
|
CFramebuffer outFB;
|
||||||
outFB->alloc(m_bufferSize.x, m_bufferSize.y, m_format);
|
outFB.alloc(m_bufferSize.x, m_bufferSize.y, m_format);
|
||||||
|
|
||||||
if (!g_pHyprRenderer->beginRender(m_pendingFrame.monitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, outFB, true)) {
|
if (!g_pHyprRenderer->beginRender(m_pendingFrame.monitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &outFB, true)) {
|
||||||
LOGM(Log::ERR, "Can't copy: failed to begin rendering to shm");
|
LOGM(Log::ERR, "Can't copy: failed to begin rendering to shm");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -182,8 +182,8 @@ bool CCursorshareSession::copy() {
|
||||||
g_pHyprRenderer->endRender();
|
g_pHyprRenderer->endRender();
|
||||||
|
|
||||||
g_pHyprOpenGL->m_renderData.pMonitor = m_pendingFrame.monitor;
|
g_pHyprOpenGL->m_renderData.pMonitor = m_pendingFrame.monitor;
|
||||||
outFB->bind();
|
outFB.bind();
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, GLFB(outFB)->getFBID());
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, outFB.getFBID());
|
||||||
|
|
||||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
|
@ -212,7 +212,7 @@ bool CCursorshareSession::copy() {
|
||||||
g_pHyprOpenGL->m_renderData.pMonitor.reset();
|
g_pHyprOpenGL->m_renderData.pMonitor.reset();
|
||||||
|
|
||||||
m_pendingFrame.buffer->endDataPtr();
|
m_pendingFrame.buffer->endDataPtr();
|
||||||
GLFB(outFB)->unbind();
|
outFB.unbind();
|
||||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
#include "../../helpers/Monitor.hpp"
|
#include "../../helpers/Monitor.hpp"
|
||||||
#include "../../desktop/view/Window.hpp"
|
#include "../../desktop/view/Window.hpp"
|
||||||
#include "../../desktop/state/FocusState.hpp"
|
#include "../../desktop/state/FocusState.hpp"
|
||||||
#include <hyprutils/math/Region.hpp>
|
|
||||||
|
|
||||||
using namespace Screenshare;
|
using namespace Screenshare;
|
||||||
|
|
||||||
|
|
@ -134,7 +133,7 @@ void CScreenshareFrame::copy() {
|
||||||
// store a snapshot before the permission popup so we don't break screenshots
|
// store a snapshot before the permission popup so we don't break screenshots
|
||||||
const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(m_session->m_client, PERMISSION_TYPE_SCREENCOPY);
|
const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(m_session->m_client, PERMISSION_TYPE_SCREENCOPY);
|
||||||
if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) {
|
if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) {
|
||||||
if (!m_session->m_tempFB || !m_session->m_tempFB->isAllocated())
|
if (!m_session->m_tempFB.isAllocated())
|
||||||
storeTempFB();
|
storeTempFB();
|
||||||
|
|
||||||
// don't copy a frame while allow is pending because screenshot tools will only take the first frame we give, which is empty
|
// don't copy a frame while allow is pending because screenshot tools will only take the first frame we give, which is empty
|
||||||
|
|
@ -160,7 +159,7 @@ void CScreenshareFrame::renderMonitor() {
|
||||||
|
|
||||||
const auto PMONITOR = m_session->monitor();
|
const auto PMONITOR = m_session->monitor();
|
||||||
|
|
||||||
auto TEXTURE = g_pHyprRenderer->createTexture(PMONITOR->m_output->state->state().buffer);
|
auto TEXTURE = makeShared<CTexture>(PMONITOR->m_output->state->state().buffer);
|
||||||
|
|
||||||
const bool IS_CM_AWARE = PROTO::colorManagement && PROTO::colorManagement->isClientCMAware(m_session->m_client);
|
const bool IS_CM_AWARE = PROTO::colorManagement && PROTO::colorManagement->isClientCMAware(m_session->m_client);
|
||||||
g_pHyprOpenGL->m_renderData.transformDamage = false;
|
g_pHyprOpenGL->m_renderData.transformDamage = false;
|
||||||
|
|
@ -296,11 +295,8 @@ void CScreenshareFrame::renderWindow() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto pointerSurface = Desktop::View::CWLSurface::fromResource(pointerSurfaceResource);
|
auto pointerSurface = Desktop::View::CWLSurface::fromResource(pointerSurfaceResource);
|
||||||
if (!pointerSurface)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto box = pointerSurface->getSurfaceBoxGlobal();
|
if (!pointerSurface || pointerSurface->getSurfaceBoxGlobal()->intersection(m_session->m_window->getFullWindowBoundingBox()).empty())
|
||||||
if (!box.has_value() || box->intersection(m_session->m_window->getFullWindowBoundingBox()).empty())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Desktop::focusState()->window() != m_session->m_window)
|
if (Desktop::focusState()->window() != m_session->m_window)
|
||||||
|
|
@ -326,10 +322,10 @@ void CScreenshareFrame::render() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_session->m_tempFB && m_session->m_tempFB->isAllocated()) {
|
if (m_session->m_tempFB.isAllocated()) {
|
||||||
CBox texbox = {{}, m_bufferSize};
|
CBox texbox = {{}, m_bufferSize};
|
||||||
g_pHyprOpenGL->renderTexture(m_session->m_tempFB->getTexture(), texbox, {});
|
g_pHyprOpenGL->renderTexture(m_session->m_tempFB.getTexture(), texbox, {});
|
||||||
m_session->m_tempFB->release();
|
m_session->m_tempFB.release();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -382,12 +378,12 @@ bool CScreenshareFrame::copyShm() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto PMONITOR = m_session->monitor();
|
const auto PMONITOR = m_session->monitor();
|
||||||
|
|
||||||
auto outFB = g_pHyprRenderer->createFB();
|
CFramebuffer outFB;
|
||||||
outFB->alloc(m_bufferSize.x, m_bufferSize.y, shm.format);
|
outFB.alloc(m_bufferSize.x, m_bufferSize.y, shm.format);
|
||||||
|
|
||||||
if (!g_pHyprRenderer->beginRender(PMONITOR, m_damage, RENDER_MODE_FULL_FAKE, nullptr, outFB, true)) {
|
if (!g_pHyprRenderer->beginRender(PMONITOR, m_damage, RENDER_MODE_FULL_FAKE, nullptr, &outFB, true)) {
|
||||||
LOGM(Log::ERR, "Can't copy: failed to begin rendering");
|
LOGM(Log::ERR, "Can't copy: failed to begin rendering");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -399,8 +395,8 @@ bool CScreenshareFrame::copyShm() {
|
||||||
g_pHyprRenderer->endRender();
|
g_pHyprRenderer->endRender();
|
||||||
|
|
||||||
g_pHyprOpenGL->m_renderData.pMonitor = PMONITOR;
|
g_pHyprOpenGL->m_renderData.pMonitor = PMONITOR;
|
||||||
outFB->bind();
|
outFB.bind();
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, GLFB(outFB)->getFBID());
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, outFB.getFBID());
|
||||||
|
|
||||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
|
@ -442,7 +438,7 @@ bool CScreenshareFrame::copyShm() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
GLFB(outFB)->unbind();
|
outFB.unbind();
|
||||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
|
@ -457,13 +453,13 @@ bool CScreenshareFrame::copyShm() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CScreenshareFrame::storeTempFB() {
|
void CScreenshareFrame::storeTempFB() {
|
||||||
if (!m_session->m_tempFB)
|
g_pHyprRenderer->makeEGLCurrent();
|
||||||
m_session->m_tempFB = g_pHyprRenderer->createFB();
|
|
||||||
m_session->m_tempFB->alloc(m_bufferSize.x, m_bufferSize.y);
|
m_session->m_tempFB.alloc(m_bufferSize.x, m_bufferSize.y);
|
||||||
|
|
||||||
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
|
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
|
||||||
|
|
||||||
if (!g_pHyprRenderer->beginRender(m_session->monitor(), fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, m_session->m_tempFB, true)) {
|
if (!g_pHyprRenderer->beginRender(m_session->monitor(), fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &m_session->m_tempFB, true)) {
|
||||||
LOGM(Log::ERR, "Can't copy: failed to begin rendering to temp fb");
|
LOGM(Log::ERR, "Can't copy: failed to begin rendering to temp fb");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,19 +145,15 @@ WP<CScreenshareSession> CScreenshareManager::getManagedSession(eScreenshareType
|
||||||
auto& session = *it;
|
auto& session = *it;
|
||||||
|
|
||||||
session->stoppedListener = session->m_session->m_events.stopped.listen([session = WP<SManagedSession>(session)]() {
|
session->stoppedListener = session->m_session->m_events.stopped.listen([session = WP<SManagedSession>(session)]() {
|
||||||
if (!session.expired())
|
std::erase_if(Screenshare::mgr()->m_managedSessions, [&](const auto& s) { return !s || session.expired() || s->m_session == session->m_session; });
|
||||||
std::erase_if(Screenshare::mgr()->m_managedSessions, [&](const auto& s) { return s && s->m_session.get() == session->m_session.get(); });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return session->m_session;
|
return session->m_session;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CScreenshareManager::isOutputBeingSSd(PHLMONITOR monitor) {
|
void CScreenshareManager::destroyClientSessions(wl_client* client) {
|
||||||
return std::ranges::any_of(m_sessions, [monitor](const auto& s) {
|
LOGM(Log::TRACE, "Destroy client sessions for {:x}", (uintptr_t)client);
|
||||||
if (!s)
|
std::erase_if(m_managedSessions, [&](const auto& session) { return !session || session->m_session->m_client == client; });
|
||||||
return false;
|
|
||||||
return (s->m_type == SHARE_MONITOR || s->m_type == SHARE_REGION) && s->m_monitor == monitor;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CScreenshareManager::SManagedSession::SManagedSession(UP<CScreenshareSession>&& session) : m_session(std::move(session)) {
|
CScreenshareManager::SManagedSession::SManagedSession(UP<CScreenshareSession>&& session) : m_session(std::move(session)) {
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ namespace Screenshare {
|
||||||
std::vector<DRMFormat> m_formats;
|
std::vector<DRMFormat> m_formats;
|
||||||
Vector2D m_bufferSize = Vector2D(0, 0);
|
Vector2D m_bufferSize = Vector2D(0, 0);
|
||||||
|
|
||||||
SP<IFramebuffer> m_tempFB;
|
CFramebuffer m_tempFB;
|
||||||
|
|
||||||
SP<CEventLoopTimer> m_shareStopTimer;
|
SP<CEventLoopTimer> m_shareStopTimer;
|
||||||
bool m_sharing = false;
|
bool m_sharing = false;
|
||||||
|
|
@ -206,8 +206,9 @@ namespace Screenshare {
|
||||||
|
|
||||||
UP<CCursorshareSession> newCursorSession(wl_client* client, WP<CWLPointerResource> pointer);
|
UP<CCursorshareSession> newCursorSession(wl_client* client, WP<CWLPointerResource> pointer);
|
||||||
|
|
||||||
|
void destroyClientSessions(wl_client* client);
|
||||||
|
|
||||||
void onOutputCommit(PHLMONITOR monitor);
|
void onOutputCommit(PHLMONITOR monitor);
|
||||||
bool isOutputBeingSSd(PHLMONITOR monitor);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<WP<CScreenshareSession>> m_sessions;
|
std::vector<WP<CScreenshareSession>> m_sessions;
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,7 @@ CScreenshareSession::CScreenshareSession(PHLMONITOR monitor, CBox captureRegion,
|
||||||
|
|
||||||
CScreenshareSession::~CScreenshareSession() {
|
CScreenshareSession::~CScreenshareSession() {
|
||||||
stop();
|
stop();
|
||||||
uintptr_t ptr = m_type == SHARE_WINDOW && !m_window.expired() ? (uintptr_t)m_window.get() : (m_monitor.expired() ? (uintptr_t)nullptr : (uintptr_t)m_monitor.get());
|
LOGM(Log::TRACE, "Destroyed screenshare session for ({}): {}", m_type, m_name);
|
||||||
LOGM(Log::TRACE, "Destroyed screenshare session for ({}): {}, {:x}", m_type, m_name, ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CScreenshareSession::stop() {
|
void CScreenshareSession::stop() {
|
||||||
|
|
@ -53,9 +52,6 @@ void CScreenshareSession::stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CScreenshareSession::init() {
|
void CScreenshareSession::init() {
|
||||||
uintptr_t ptr = m_type == SHARE_WINDOW && !m_window.expired() ? (uintptr_t)m_window.get() : (m_monitor.expired() ? (uintptr_t)nullptr : (uintptr_t)m_monitor.get());
|
|
||||||
LOGM(Log::TRACE, "Created screenshare session for ({}): {}, {:x}", m_type, m_name, ptr);
|
|
||||||
|
|
||||||
m_shareStopTimer = makeShared<CEventLoopTimer>(
|
m_shareStopTimer = makeShared<CEventLoopTimer>(
|
||||||
std::chrono::milliseconds(500),
|
std::chrono::milliseconds(500),
|
||||||
[this](SP<CEventLoopTimer> self, void* data) {
|
[this](SP<CEventLoopTimer> self, void* data) {
|
||||||
|
|
@ -103,7 +99,7 @@ void CScreenshareSession::calculateConstraints() {
|
||||||
m_name = PMONITOR->m_name;
|
m_name = PMONITOR->m_name;
|
||||||
break;
|
break;
|
||||||
case SHARE_WINDOW:
|
case SHARE_WINDOW:
|
||||||
m_bufferSize = (m_window->m_realSize->value() * PMONITOR->m_scale).round();
|
m_bufferSize = m_window->m_realSize->value().round();
|
||||||
m_name = m_window->m_title;
|
m_name = m_window->m_title;
|
||||||
break;
|
break;
|
||||||
case SHARE_REGION:
|
case SHARE_REGION:
|
||||||
|
|
@ -125,7 +121,7 @@ void CScreenshareSession::screenshareEvents(bool startSharing) {
|
||||||
m_sharing = true;
|
m_sharing = true;
|
||||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "screencast", .data = std::format("1,{}", m_type)});
|
g_pEventManager->postEvent(SHyprIPCEvent{.event = "screencast", .data = std::format("1,{}", m_type)});
|
||||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "screencastv2", .data = std::format("1,{},{}", m_type, m_name)});
|
g_pEventManager->postEvent(SHyprIPCEvent{.event = "screencastv2", .data = std::format("1,{},{}", m_type, m_name)});
|
||||||
LOGM(Log::INFO, "Started screenshare session for ({}): {}", m_type, m_name);
|
LOGM(Log::INFO, "New screenshare session for ({}): {}", m_type, m_name);
|
||||||
|
|
||||||
Event::bus()->m_events.screenshare.state.emit(true, m_type, m_name);
|
Event::bus()->m_events.screenshare.state.emit(true, m_type, m_name);
|
||||||
} else if (!startSharing && m_sharing) {
|
} else if (!startSharing && m_sharing) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include "color-management-v1.hpp"
|
#include "color-management-v1.hpp"
|
||||||
#include "../helpers/Monitor.hpp"
|
#include "../helpers/Monitor.hpp"
|
||||||
#include "core/Output.hpp"
|
#include "core/Output.hpp"
|
||||||
#include "../helpers/cm/ColorManagement.hpp"
|
#include "types/ColorManagement.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
using namespace NColorManagement;
|
using namespace NColorManagement;
|
||||||
|
|
@ -388,6 +388,12 @@ CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CWpColorMana
|
||||||
RESOURCE->m_settings = m_surface->getPreferredImageDescription();
|
RESOURCE->m_settings = m_surface->getPreferredImageDescription();
|
||||||
m_currentPreferredId = RESOURCE->m_settings->id();
|
m_currentPreferredId = RESOURCE->m_settings->id();
|
||||||
|
|
||||||
|
if (!PROTO::colorManagement->m_debug && RESOURCE->m_settings->value().icc.fd >= 0) {
|
||||||
|
LOGM(Log::ERR, "FIXME: parse icc profile");
|
||||||
|
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
RESOURCE->resource()->sendReady(m_currentPreferredId);
|
RESOURCE->resource()->sendReady(m_currentPreferredId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -423,7 +429,7 @@ CColorManagementIccCreator::CColorManagementIccCreator(SP<CWpImageDescriptionCre
|
||||||
LOGM(Log::TRACE, "Create image description from icc for id {}", id);
|
LOGM(Log::TRACE, "Create image description from icc for id {}", id);
|
||||||
|
|
||||||
// FIXME actually check completeness
|
// FIXME actually check completeness
|
||||||
if (m_icc.fd < 0 || !m_icc.length) {
|
if (m_settings.icc.fd < 0 || !m_settings.icc.length) {
|
||||||
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INCOMPLETE_SET, "Missing required settings");
|
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INCOMPLETE_SET, "Missing required settings");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -437,10 +443,10 @@ CColorManagementIccCreator::CColorManagementIccCreator(SP<CWpImageDescriptionCre
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGM(Log::ERR, "FIXME: Parse icc file {}({},{}) for id {}", m_icc.fd, m_icc.offset, m_icc.length, id);
|
LOGM(Log::ERR, "FIXME: Parse icc file {}({},{}) for id {}", m_settings.icc.fd, m_settings.icc.offset, m_settings.icc.length, id);
|
||||||
|
|
||||||
// FIXME actually check support
|
// FIXME actually check support
|
||||||
if (m_icc.fd < 0 || !m_icc.length) {
|
if (m_settings.icc.fd < 0 || !m_settings.icc.length) {
|
||||||
RESOURCE->resource()->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_UNSUPPORTED, "unsupported");
|
RESOURCE->resource()->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_UNSUPPORTED, "unsupported");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -453,9 +459,9 @@ CColorManagementIccCreator::CColorManagementIccCreator(SP<CWpImageDescriptionCre
|
||||||
});
|
});
|
||||||
|
|
||||||
m_resource->setSetIccFile([this](CWpImageDescriptionCreatorIccV1* r, int fd, uint32_t offset, uint32_t length) {
|
m_resource->setSetIccFile([this](CWpImageDescriptionCreatorIccV1* r, int fd, uint32_t offset, uint32_t length) {
|
||||||
m_icc.fd = fd;
|
m_settings.icc.fd = fd;
|
||||||
m_icc.offset = offset;
|
m_settings.icc.offset = offset;
|
||||||
m_icc.length = length;
|
m_settings.icc.length = length;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -725,9 +731,8 @@ CColorManagementImageDescriptionInfo::CColorManagementImageDescriptionInfo(SP<CW
|
||||||
|
|
||||||
const auto toProto = [](float value) { return sc<int32_t>(std::round(value * PRIMARIES_SCALE)); };
|
const auto toProto = [](float value) { return sc<int32_t>(std::round(value * PRIMARIES_SCALE)); };
|
||||||
|
|
||||||
// FIXME:
|
if (m_settings.icc.fd >= 0)
|
||||||
// if (m_icc.fd >= 0)
|
m_resource->sendIccFile(m_settings.icc.fd, m_settings.icc.length);
|
||||||
// m_resource->sendIccFile(m_icc.fd, m_icc.length);
|
|
||||||
|
|
||||||
// send preferred client paramateres
|
// send preferred client paramateres
|
||||||
m_resource->sendPrimaries(toProto(m_settings.primaries.red.x), toProto(m_settings.primaries.red.y), toProto(m_settings.primaries.green.x),
|
m_resource->sendPrimaries(toProto(m_settings.primaries.red.x), toProto(m_settings.primaries.red.y), toProto(m_settings.primaries.green.x),
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#include "../helpers/Monitor.hpp"
|
#include "../helpers/Monitor.hpp"
|
||||||
#include "core/Compositor.hpp"
|
#include "core/Compositor.hpp"
|
||||||
#include "color-management-v1.hpp"
|
#include "color-management-v1.hpp"
|
||||||
#include "../helpers/cm/ColorManagement.hpp"
|
#include "types/ColorManagement.hpp"
|
||||||
|
|
||||||
class CColorManager;
|
class CColorManager;
|
||||||
class CColorManagementOutput;
|
class CColorManagementOutput;
|
||||||
|
|
@ -109,14 +109,6 @@ class CColorManagementIccCreator {
|
||||||
WP<CColorManagementIccCreator> m_self;
|
WP<CColorManagementIccCreator> m_self;
|
||||||
|
|
||||||
NColorManagement::SImageDescription m_settings;
|
NColorManagement::SImageDescription m_settings;
|
||||||
struct SIccFile {
|
|
||||||
int fd = -1;
|
|
||||||
uint32_t length = 0;
|
|
||||||
uint32_t offset = 0;
|
|
||||||
bool operator==(const SIccFile& i2) const {
|
|
||||||
return fd == i2.fd;
|
|
||||||
}
|
|
||||||
} m_icc;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SP<CWpImageDescriptionCreatorIccV1> m_resource;
|
SP<CWpImageDescriptionCreatorIccV1> m_resource;
|
||||||
|
|
|
||||||
|
|
@ -56,12 +56,6 @@ CGammaControl::CGammaControl(SP<CZwlrGammaControlV1> resource_, wl_resource* out
|
||||||
|
|
||||||
LOGM(Log::DEBUG, "setGamma for {}", m_monitor->m_name);
|
LOGM(Log::DEBUG, "setGamma for {}", m_monitor->m_name);
|
||||||
|
|
||||||
if UNLIKELY (m_monitor->gammaRampsInUse()) {
|
|
||||||
LOGM(Log::ERR, "Monitor has gamma ramps in use (ICC?)");
|
|
||||||
m_resource->sendFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: make CFileDescriptor getflags use F_GETFL
|
// TODO: make CFileDescriptor getflags use F_GETFL
|
||||||
int fdFlags = fcntl(gammaFd.get(), F_GETFL, 0);
|
int fdFlags = fcntl(gammaFd.get(), F_GETFL, 0);
|
||||||
if UNLIKELY (fdFlags < 0) {
|
if UNLIKELY (fdFlags < 0) {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,10 @@ CScreencopyClient::CScreencopyClient(SP<CZwlrScreencopyManagerV1> resource_) : m
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_resource->setDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); });
|
m_resource->setDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); });
|
||||||
m_resource->setOnDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); });
|
m_resource->setOnDestroy([this](CZwlrScreencopyManagerV1* pMgr) {
|
||||||
|
Screenshare::mgr()->destroyClientSessions(m_savedClient);
|
||||||
|
PROTO::screencopy->destroyResource(this);
|
||||||
|
});
|
||||||
m_resource->setCaptureOutput(
|
m_resource->setCaptureOutput(
|
||||||
[this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output) { captureOutput(frame, overlayCursor, output, {}); });
|
[this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output) { captureOutput(frame, overlayCursor, output, {}); });
|
||||||
m_resource->setCaptureOutputRegion([this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output, int32_t x, int32_t y, int32_t w,
|
m_resource->setCaptureOutputRegion([this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output, int32_t x, int32_t y, int32_t w,
|
||||||
|
|
@ -22,6 +25,10 @@ CScreencopyClient::CScreencopyClient(SP<CZwlrScreencopyManagerV1> resource_) : m
|
||||||
m_savedClient = m_resource->client();
|
m_savedClient = m_resource->client();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CScreencopyClient::~CScreencopyClient() {
|
||||||
|
Screenshare::mgr()->destroyClientSessions(m_savedClient);
|
||||||
|
}
|
||||||
|
|
||||||
void CScreencopyClient::captureOutput(uint32_t frame, int32_t overlayCursor_, wl_resource* output, CBox box) {
|
void CScreencopyClient::captureOutput(uint32_t frame, int32_t overlayCursor_, wl_resource* output, CBox box) {
|
||||||
const auto PMONITORRES = CWLOutputResource::fromResource(output);
|
const auto PMONITORRES = CWLOutputResource::fromResource(output);
|
||||||
if (!PMONITORRES || !PMONITORRES->m_monitor) {
|
if (!PMONITORRES || !PMONITORRES->m_monitor) {
|
||||||
|
|
@ -62,6 +69,11 @@ CScreencopyFrame::CScreencopyFrame(SP<CZwlrScreencopyFrameV1> resource_, WP<CScr
|
||||||
m_resource->setCopy([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) { shareFrame(pFrame, res, false); });
|
m_resource->setCopy([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) { shareFrame(pFrame, res, false); });
|
||||||
m_resource->setCopyWithDamage([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) { shareFrame(pFrame, res, true); });
|
m_resource->setCopyWithDamage([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) { shareFrame(pFrame, res, true); });
|
||||||
|
|
||||||
|
m_listeners.stopped = m_session->m_events.stopped.listen([this]() {
|
||||||
|
if (good())
|
||||||
|
m_resource->sendFailed();
|
||||||
|
});
|
||||||
|
|
||||||
m_frame = m_session->nextFrame(overlayCursor);
|
m_frame = m_session->nextFrame(overlayCursor);
|
||||||
|
|
||||||
auto formats = m_session->allowedFormats();
|
auto formats = m_session->allowedFormats();
|
||||||
|
|
@ -75,14 +87,7 @@ CScreencopyFrame::CScreencopyFrame(SP<CZwlrScreencopyFrameV1> resource_, WP<CScr
|
||||||
auto bufSize = m_frame->bufferSize();
|
auto bufSize = m_frame->bufferSize();
|
||||||
|
|
||||||
const auto PSHMINFO = NFormatUtils::getPixelFormatFromDRM(format);
|
const auto PSHMINFO = NFormatUtils::getPixelFormatFromDRM(format);
|
||||||
|
const auto stride = NFormatUtils::minStride(PSHMINFO, bufSize.x);
|
||||||
if (!PSHMINFO) {
|
|
||||||
LOGM(Log::ERR, "No pixel format for drm format");
|
|
||||||
m_resource->sendFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto stride = NFormatUtils::minStride(PSHMINFO, bufSize.x);
|
|
||||||
m_resource->sendBuffer(NFormatUtils::drmToShm(format), bufSize.x, bufSize.y, stride);
|
m_resource->sendBuffer(NFormatUtils::drmToShm(format), bufSize.x, bufSize.y, stride);
|
||||||
|
|
||||||
if (m_resource->version() >= 3) {
|
if (m_resource->version() >= 3) {
|
||||||
|
|
@ -99,12 +104,6 @@ void CScreencopyFrame::shareFrame(CZwlrScreencopyFrameV1* pFrame, wl_resource* b
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if UNLIKELY (m_session.expired() || !m_session->monitor()) {
|
|
||||||
LOGM(Log::ERR, "Session stopped for frame {:x}", (uintptr_t)this);
|
|
||||||
m_resource->sendFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if UNLIKELY (m_buffer) {
|
if UNLIKELY (m_buffer) {
|
||||||
LOGM(Log::ERR, "Buffer used in {:x}", (uintptr_t)this);
|
LOGM(Log::ERR, "Buffer used in {:x}", (uintptr_t)this);
|
||||||
m_resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
|
m_resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ namespace Screenshare {
|
||||||
class CScreencopyClient {
|
class CScreencopyClient {
|
||||||
public:
|
public:
|
||||||
CScreencopyClient(SP<CZwlrScreencopyManagerV1> resource_);
|
CScreencopyClient(SP<CZwlrScreencopyManagerV1> resource_);
|
||||||
|
~CScreencopyClient();
|
||||||
|
|
||||||
bool good();
|
bool good();
|
||||||
|
|
||||||
|
|
@ -51,7 +52,10 @@ class CScreencopyFrame {
|
||||||
Time::steady_tp m_timestamp;
|
Time::steady_tp m_timestamp;
|
||||||
bool m_overlayCursor = true;
|
bool m_overlayCursor = true;
|
||||||
|
|
||||||
//
|
struct {
|
||||||
|
CHyprSignalListener stopped;
|
||||||
|
} m_listeners;
|
||||||
|
|
||||||
void shareFrame(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer, bool withDamage);
|
void shareFrame(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer, bool withDamage);
|
||||||
|
|
||||||
friend class CScreencopyProtocol;
|
friend class CScreencopyProtocol;
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@ CSinglePixelBuffer::CSinglePixelBuffer(uint32_t id, wl_client* client, CHyprColo
|
||||||
|
|
||||||
m_opaque = col_.a >= 1.F;
|
m_opaque = col_.a >= 1.F;
|
||||||
|
|
||||||
m_texture = g_pHyprRenderer->createTexture(DRM_FORMAT_ARGB8888, rc<uint8_t*>(&m_color), 4, Vector2D{1, 1});
|
m_texture = makeShared<CTexture>(DRM_FORMAT_ARGB8888, rc<uint8_t*>(&m_color), 4, Vector2D{1, 1});
|
||||||
|
|
||||||
m_resource = CWLBufferResource::create(makeShared<CWlBuffer>(client, 1, id));
|
m_resource = CWLBufferResource::create(makeShared<CWlBuffer>(client, 1, id));
|
||||||
|
|
||||||
m_success = m_texture->ok();
|
m_success = m_texture->m_texID;
|
||||||
|
|
||||||
size = {1, 1};
|
size = {1, 1};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,10 @@ CToplevelExportClient::CToplevelExportClient(SP<CHyprlandToplevelExportManagerV1
|
||||||
if UNLIKELY (!good())
|
if UNLIKELY (!good())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_resource->setOnDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) { PROTO::toplevelExport->destroyResource(this); });
|
m_resource->setOnDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) {
|
||||||
|
Screenshare::mgr()->destroyClientSessions(m_savedClient);
|
||||||
|
PROTO::toplevelExport->destroyResource(this);
|
||||||
|
});
|
||||||
m_resource->setDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) { PROTO::toplevelExport->destroyResource(this); });
|
m_resource->setDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) { PROTO::toplevelExport->destroyResource(this); });
|
||||||
m_resource->setCaptureToplevel([this](CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, uint32_t handle) {
|
m_resource->setCaptureToplevel([this](CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, uint32_t handle) {
|
||||||
captureToplevel(frame, overlayCursor, g_pCompositor->getWindowFromHandle(handle));
|
captureToplevel(frame, overlayCursor, g_pCompositor->getWindowFromHandle(handle));
|
||||||
|
|
@ -25,6 +28,10 @@ CToplevelExportClient::CToplevelExportClient(SP<CHyprlandToplevelExportManagerV1
|
||||||
m_savedClient = m_resource->client();
|
m_savedClient = m_resource->client();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CToplevelExportClient::~CToplevelExportClient() {
|
||||||
|
Screenshare::mgr()->destroyClientSessions(m_savedClient);
|
||||||
|
}
|
||||||
|
|
||||||
void CToplevelExportClient::captureToplevel(uint32_t frame, int32_t overlayCursor_, PHLWINDOW handle) {
|
void CToplevelExportClient::captureToplevel(uint32_t frame, int32_t overlayCursor_, PHLWINDOW handle) {
|
||||||
auto session = Screenshare::mgr()->getManagedSession(m_resource->client(), handle);
|
auto session = Screenshare::mgr()->getManagedSession(m_resource->client(), handle);
|
||||||
|
|
||||||
|
|
@ -56,6 +63,11 @@ CToplevelExportFrame::CToplevelExportFrame(SP<CHyprlandToplevelExportFrameV1> re
|
||||||
m_resource->setDestroy([this](CHyprlandToplevelExportFrameV1* pFrame) { PROTO::toplevelExport->destroyResource(this); });
|
m_resource->setDestroy([this](CHyprlandToplevelExportFrameV1* pFrame) { PROTO::toplevelExport->destroyResource(this); });
|
||||||
m_resource->setCopy([this](CHyprlandToplevelExportFrameV1* pFrame, wl_resource* res, int32_t ignoreDamage) { shareFrame(res, !!ignoreDamage); });
|
m_resource->setCopy([this](CHyprlandToplevelExportFrameV1* pFrame, wl_resource* res, int32_t ignoreDamage) { shareFrame(res, !!ignoreDamage); });
|
||||||
|
|
||||||
|
m_listeners.stopped = m_session->m_events.stopped.listen([this]() {
|
||||||
|
if (good())
|
||||||
|
m_resource->sendFailed();
|
||||||
|
});
|
||||||
|
|
||||||
m_frame = m_session->nextFrame(overlayCursor);
|
m_frame = m_session->nextFrame(overlayCursor);
|
||||||
|
|
||||||
auto formats = m_session->allowedFormats();
|
auto formats = m_session->allowedFormats();
|
||||||
|
|
@ -88,12 +100,6 @@ void CToplevelExportFrame::shareFrame(wl_resource* buffer, bool ignoreDamage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if UNLIKELY (m_session.expired() || !m_session->monitor()) {
|
|
||||||
LOGM(Log::ERR, "Session stopped for frame {:x}", (uintptr_t)this);
|
|
||||||
m_resource->sendFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if UNLIKELY (m_buffer) {
|
if UNLIKELY (m_buffer) {
|
||||||
LOGM(Log::ERR, "Buffer used in {:x}", (uintptr_t)this);
|
LOGM(Log::ERR, "Buffer used in {:x}", (uintptr_t)this);
|
||||||
m_resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
|
m_resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ namespace Screenshare {
|
||||||
class CToplevelExportClient {
|
class CToplevelExportClient {
|
||||||
public:
|
public:
|
||||||
CToplevelExportClient(SP<CHyprlandToplevelExportManagerV1> resource_);
|
CToplevelExportClient(SP<CHyprlandToplevelExportManagerV1> resource_);
|
||||||
|
~CToplevelExportClient();
|
||||||
|
|
||||||
bool good();
|
bool good();
|
||||||
|
|
||||||
|
|
@ -49,7 +50,10 @@ class CToplevelExportFrame {
|
||||||
CHLBufferReference m_buffer;
|
CHLBufferReference m_buffer;
|
||||||
Time::steady_tp m_timestamp;
|
Time::steady_tp m_timestamp;
|
||||||
|
|
||||||
//
|
struct {
|
||||||
|
CHyprSignalListener stopped;
|
||||||
|
} m_listeners;
|
||||||
|
|
||||||
void shareFrame(wl_resource* buffer, bool ignoreDamage);
|
void shareFrame(wl_resource* buffer, bool ignoreDamage);
|
||||||
|
|
||||||
friend class CToplevelExportProtocol;
|
friend class CToplevelExportProtocol;
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
#include "../../helpers/math/Math.hpp"
|
#include "../../helpers/math/Math.hpp"
|
||||||
#include "../../helpers/time/Time.hpp"
|
#include "../../helpers/time/Time.hpp"
|
||||||
#include "../types/Buffer.hpp"
|
#include "../types/Buffer.hpp"
|
||||||
#include "../../helpers/cm/ColorManagement.hpp"
|
#include "../types/ColorManagement.hpp"
|
||||||
#include "../types/SurfaceRole.hpp"
|
#include "../types/SurfaceRole.hpp"
|
||||||
#include "../types/SurfaceState.hpp"
|
#include "../types/SurfaceState.hpp"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class IHLBuffer : public Aquamarine::IBuffer {
|
||||||
void onBackendRelease(const std::function<void()>& fn);
|
void onBackendRelease(const std::function<void()>& fn);
|
||||||
void addReleasePoint(CDRMSyncPointState& point);
|
void addReleasePoint(CDRMSyncPointState& point);
|
||||||
|
|
||||||
SP<ITexture> m_texture;
|
SP<CTexture> m_texture;
|
||||||
bool m_opaque = false;
|
bool m_opaque = false;
|
||||||
SP<CWLBufferResource> m_resource;
|
SP<CWLBufferResource> m_resource;
|
||||||
std::vector<UP<CSyncReleaser>> m_syncReleasers;
|
std::vector<UP<CSyncReleaser>> m_syncReleasers;
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue