Compare commits

..

No commits in common. "main" and "v0.54.0" have entirely different histories.

176 changed files with 3163 additions and 6240 deletions

View file

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

View file

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

@ -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": {

View file

@ -88,28 +88,23 @@
}; };
}; };
outputs = outputs = inputs @ {
inputs@{
self, self,
nixpkgs, nixpkgs,
systems, systems,
... ...
}: }: let
let
inherit (nixpkgs) lib; inherit (nixpkgs) lib;
eachSystem = lib.genAttrs (import systems); eachSystem = lib.genAttrs (import systems);
pkgsFor = eachSystem ( pkgsFor = eachSystem (system:
system:
import nixpkgs { import nixpkgs {
localSystem = system; localSystem = system;
overlays = with self.overlays; [ overlays = with self.overlays; [
hyprland-packages hyprland-packages
hyprland-extras hyprland-extras
]; ];
} });
); pkgsCrossFor = eachSystem (system: crossSystem:
pkgsCrossFor = eachSystem (
system: crossSystem:
import nixpkgs { import nixpkgs {
localSystem = system; localSystem = system;
inherit crossSystem; inherit crossSystem;
@ -117,36 +112,29 @@
hyprland-packages hyprland-packages
hyprland-extras hyprland-extras
]; ];
} });
); pkgsDebugFor = eachSystem (system:
pkgsDebugFor = eachSystem (
system:
import nixpkgs { import nixpkgs {
localSystem = system; localSystem = system;
overlays = with self.overlays; [ overlays = with self.overlays; [
hyprland-debug hyprland-debug
]; ];
} });
); pkgsDebugCrossFor = eachSystem (system: crossSystem:
pkgsDebugCrossFor = eachSystem (
system: crossSystem:
import nixpkgs { import nixpkgs {
localSystem = system; localSystem = system;
inherit crossSystem; inherit crossSystem;
overlays = with self.overlays; [ overlays = with self.overlays; [
hyprland-debug hyprland-debug
]; ];
} });
); in {
in
{
overlays = import ./nix/overlays.nix {inherit self lib inputs;}; 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 {
@ -162,12 +150,12 @@
}; };
}; };
} }
// (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
(pkgsFor.${system})
# hyprland-packages # hyprland-packages
hyprland hyprland
hyprland-unwrapped hyprland-unwrapped
@ -182,11 +170,9 @@
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"; name = "hyprland-shell";
hardeningDisable = ["fortify"]; hardeningDisable = ["fortify"];
inputsFrom = [pkgsFor.${system}.hyprland]; inputsFrom = [pkgsFor.${system}.hyprland];

View file

@ -228,9 +228,6 @@ 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
// this handles partial writes on the server side under high load
while (true) {
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE); sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
if (sizeWritten < 0) { if (sizeWritten < 0) {
@ -240,11 +237,14 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) {
return 6; return 6;
} }
if (sizeWritten == 0) { reply += std::string(buffer, sizeWritten);
// server closed connection, we're done
break;
}
while (sizeWritten == BUFFER_SIZE) {
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
if (sizeWritten < 0) {
log("Couldn't read (6)");
return 6;
}
reply += std::string(buffer, sizeWritten); reply += std::string(buffer, sizeWritten);
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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");

View file

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

View file

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

View file

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

View file

@ -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"));

View file

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

View file

@ -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"));

View file

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

View file

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

View file

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

View file

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

View file

@ -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,8 +90,7 @@ 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
@ -122,12 +106,8 @@ in
(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 [ (optional withTests [../tests ../hyprtester])
../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 ''

View file

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

View file

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

View file

@ -1,5 +1,4 @@
lib: lib: let
let
inherit (lib) inherit (lib)
attrNames attrNames
filterAttrs filterAttrs
@ -82,19 +81,11 @@ let
::: :::
*/ */
toHyprlang = toHyprlang = {
{ topCommandsPrefixes ? ["$" "bezier"],
topCommandsPrefixes ? [
"$"
"bezier"
],
bottomCommandsPrefixes ? [], bottomCommandsPrefixes ? [],
}: }: attrs: let
attrs: toHyprlang' = attrs: let
let
toHyprlang' =
attrs:
let
# Specially configured `toKeyValue` generator with support for duplicate keys # Specially configured `toKeyValue` generator with support for duplicate keys
# and a legible key-value separator. # and a legible key-value separator.
mkCommands = generators.toKeyValue { mkCommands = generators.toKeyValue {
@ -108,7 +99,8 @@ let
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);
@ -182,19 +174,24 @@ let
``` ```
::: :::
*/ */
flattenAttrs = flattenAttrs = pred: attrs: let
pred: attrs: flattenAttrs' = prefix: attrs:
let
flattenAttrs' =
prefix: attrs:
builtins.foldl' ( builtins.foldl' (
acc: key: acc: key: let
let
value = attrs.${key}; value = attrs.${key};
newKey = if prefix == "" then key else pred prefix key; newKey =
if prefix == ""
then key
else pred prefix key;
in in
acc // (if builtins.isAttrs value then flattenAttrs' newKey value else { "${newKey}" = value; }) acc
// (
if builtins.isAttrs value
then flattenAttrs' newKey value
else {"${newKey}" = value;}
)
) {} (builtins.attrNames attrs); ) {} (builtins.attrNames attrs);
in in
flattenAttrs' "" attrs; flattenAttrs' "" attrs;

View file

@ -1,16 +1,13 @@
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 {
@ -23,9 +20,7 @@ in
}; };
settings = lib.mkOption { settings = lib.mkOption {
type = type = with lib.types; let
with lib.types;
let
valueType = valueType =
nullOr (oneOf [ nullOr (oneOf [
bool bool
@ -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.
''; '';
@ -129,22 +117,20 @@ 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; topCommandsPrefixes = cfg.topPrefixes;
bottomCommandsPrefixes = cfg.bottomPrefixes; bottomCommandsPrefixes = cfg.bottomPrefixes;
} }
{ {
"exec-once" = "exec-once" = let
let mkEntry = entry:
mkEntry = if lib.types.package.check entry
entry: if lib.types.package.check entry then "${entry}/lib/lib${entry.pname}.so" else entry; then "${entry}/lib/lib${entry.pname}.so"
else entry;
hyprctl = lib.getExe' config.programs.hyprland.package "hyprctl"; hyprctl = lib.getExe' config.programs.hyprland.package "hyprctl";
in in
map (p: "${hyprctl} plugin load ${mkEntry p}") cfg.plugins; map (p: "${hyprctl} plugin load ${mkEntry p}") cfg.plugins;
@ -152,13 +138,14 @@ in
in in
lib.mkIf shouldGenerate { lib.mkIf shouldGenerate {
text = text =
lib.optionalString (cfg.plugins != [ ]) (pluginsToHyprlang cfg.plugins) lib.optionalString (cfg.plugins != [])
+ lib.optionalString (cfg.settings != { }) ( (pluginsToHyprlang cfg.plugins)
selflib.toHyprlang { + lib.optionalString (cfg.settings != {})
(selflib.toHyprlang {
topCommandsPrefixes = cfg.topPrefixes; topCommandsPrefixes = cfg.topPrefixes;
bottomCommandsPrefixes = cfg.bottomPrefixes; bottomCommandsPrefixes = cfg.bottomPrefixes;
} cfg.settings }
) cfg.settings)
+ lib.optionalString (cfg.extraConfig != "") cfg.extraConfig; + lib.optionalString (cfg.extraConfig != "") cfg.extraConfig;
}; };
}) })

View file

@ -2,27 +2,20 @@
self, self,
lib, lib,
inputs, inputs,
}: }: let
let mkDate = longDate: (lib.concatStringsSep "-" [
mkDate =
longDate:
(lib.concatStringsSep "-" [
(builtins.substring 0 4 longDate) (builtins.substring 0 4 longDate)
(builtins.substring 4 2 longDate) (builtins.substring 4 2 longDate)
(builtins.substring 6 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-packages
hyprland-extras 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,13 +33,10 @@ in
self.overlays.glaze self.overlays.glaze
# Hyprland packages themselves # Hyprland packages themselves
( (final: _prev: let
final: _prev:
let
date = mkDate (self.lastModifiedDate or "19700101"); date = mkDate (self.lastModifiedDate or "19700101");
version = "${ver}+date=${date}_${self.shortRev or "dirty"}"; version = "${ver}+date=${date}_${self.shortRev or "dirty"}";
in in {
{
hyprland = final.callPackage ./default.nix { hyprland = final.callPackage ./default.nix {
stdenv = final.gcc15Stdenv; stdenv = final.gcc15Stdenv;
commit = self.rev or ""; commit = self.rev or "";
@ -57,28 +47,35 @@ in
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,7 +83,7 @@ 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,8 +100,7 @@ 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";
@ -113,13 +109,12 @@ in
}; };
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;

View file

@ -1,15 +1,11 @@
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; [ environment.systemPackages = with pkgs; [
# Programs needed for tests # Programs needed for tests
jq jq

View file

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

View file

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

View file

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

View file

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

View file

@ -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]));

View file

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

View file

@ -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();
size_t waitsDone = 0;
constexpr const size_t MAX_WAITS = 20; // 2000µs = 2ms
while (totalWritten < data.length()) {
ssize_t written = write(fd, data.c_str() + totalWritten, remaining);
if (waitsDone > MAX_WAITS) {
Log::logger->log(Log::ERR, "Couldn't write to socket. Buffer was full and the client couldn't read in time.");
return false;
}
if (written < 0) {
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; return true;
if (errno == EAGAIN)
return true;
if (needLog)
Log::logger->log(Log::ERR, "Couldn't write to socket. Error: " + std::string(strerror(errno)));
return false;
} }
static void runWritingDebugLogThread(const int conn) { static void runWritingDebugLogThread(const int conn) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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();

View file

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

View file

@ -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(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); 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;
} }
@ -1952,9 +1953,7 @@ void CWindow::mapWindow() {
&& 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());

View file

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

View file

@ -297,12 +297,24 @@ 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;
@ -310,10 +322,6 @@ std::string NFormatUtils::drmFormatName(DRMFormat drm) {
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;

View file

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

View file

@ -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,15 +290,9 @@ 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;
if (BACKUPMON) {
// snap cursor
g_pCompositor->warpCursorTo(BACKUPMON->m_position + BACKUPMON->m_transformedSize / 2.F, true);
// move workspaces
std::vector<PHLWORKSPACE> wspToMove; std::vector<PHLWORKSPACE> wspToMove;
for (auto const& w : g_pCompositor->getWorkspaces()) { for (auto const& w : g_pCompositor->getWorkspaces()) {
if (w->m_monitor == m_self || !w->m_monitor) if (w->m_monitor == m_self || !w->m_monitor)
wspToMove.emplace_back(w.lock()); 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) { for (auto const& w : wspToMove) {
if (w && w->m_lastMonitor.empty())
w->m_lastMonitor = m_name; w->m_lastMonitor = m_name;
}
if (BACKUPMON) {
// snap cursor
g_pCompositor->warpCursorTo(BACKUPMON->m_position + BACKUPMON->m_transformedSize / 2.F, true);
for (auto const& w : wspToMove) {
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,9 +919,6 @@ 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()) {
// only apply explicit cm settings if we have no icc file
m_cmType = RULE->cmType; m_cmType = RULE->cmType;
switch (m_cmType) { switch (m_cmType) {
case NCMType::CM_AUTO: m_cmType = m_enabled10bit && supportsWideColor() ? NCMType::CM_WIDE : NCMType::CM_SRGB; break; case NCMType::CM_AUTO: m_cmType = m_enabled10bit && supportsWideColor() ? NCMType::CM_WIDE : NCMType::CM_SRGB; break;
@ -958,20 +941,6 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
m_sdrSaturation = RULE->sdrSaturation; m_sdrSaturation = RULE->sdrSaturation;
m_sdrBrightness = RULE->sdrBrightness; 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{});
}
}
}
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))) {
@ -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) {
; ;
} }

View file

@ -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,7 +353,7 @@ 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 {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)");

View file

@ -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();

View file

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

View file

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

View file

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

View file

@ -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();

View file

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

View file

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

View file

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

View file

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

View file

@ -99,12 +99,10 @@ 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);
else if (!m_overrideFocalPoint && ACTIVE_WINDOW && !ACTIVE_WINDOW->m_isFloating && ACTIVE_WINDOW != target->window() && ACTIVE_WINDOW->m_workspace == PWORKSPACE &&
ACTIVE_WINDOW->m_isMapped) ACTIVE_WINDOW->m_isMapped)
OPENINGON = getNodeFromWindow(ACTIVE_WINDOW); OPENINGON = getNodeFromWindow(ACTIVE_WINDOW);
@ -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,11 +242,12 @@ 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
if (!verticalOverride && (NEWPARENT->box.w * *PWIDTHMULTIPLIER > NEWPARENT->box.h || horizontalOverride)) { if (!verticalOverride && (NEWPARENT->box.w * *PWIDTHMULTIPLIER > NEWPARENT->box.h || horizontalOverride)) {
@ -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;
} }

View file

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

View file

@ -403,8 +403,6 @@ 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())
@ -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,7 +720,7 @@ 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,7 +756,7 @@ 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});

View file

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

View file

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

View file

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

View file

@ -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; void SColumnData::down(SP<SScrollingTargetData> w) {
}
bool 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() {
static const auto PCOLWIDTH = CConfigValue<Hyprlang::FLOAT>("scrolling:column_width");
auto col = columns.emplace_back(makeShared<SColumnData>(self.lock())); auto col = columns.emplace_back(makeShared<SColumnData>(self.lock()));
col->self = col; 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) {
static const auto PCOLWIDTH = CConfigValue<Hyprlang::FLOAT>("scrolling:column_width");
auto col = makeShared<SColumnData>(self.lock()); auto col = makeShared<SColumnData>(self.lock());
col->self = col; 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));
} }
@ -628,9 +617,7 @@ 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);
@ -638,9 +625,7 @@ void CScrollingAlgorithm::removeTarget(SP<ITarget> 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,63 +833,21 @@ 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()) {
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()); const auto COL = m_scrollingData->prev(DATA->column.lock());
// ignore moves to the origin if we are alone // ignore moves to the "origin" when on first column and moving opposite to tape direction
if (!COL && current_idx == 0 && DATA->column->targetDatas.size() == 1) if (!COL && current_idx == 0 && (TAPE_DIR == SCROLL_DIR_RIGHT || TAPE_DIR == SCROLL_DIR_DOWN))
return false; return;
DATA->column->remove(t); DATA->column->remove(t);
@ -919,14 +862,12 @@ void CScrollingAlgorithm::moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool
COL->add(DATA); COL->add(DATA);
m_scrollingData->centerOrFitCol(COL); m_scrollingData->centerOrFitCol(COL);
} }
} else if (dir == Math::DIRECTION_RIGHT) {
return true;
} else if (ROTATED_DIR == Math::DIRECTION_RIGHT) {
const auto COL = m_scrollingData->next(DATA->column.lock()); const auto COL = m_scrollingData->next(DATA->column.lock());
// ignore move to the right when there is no next column and we're alone // 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 && DATA->column->targetDatas.size() == 1) if (!COL && current_idx == (int64_t)m_scrollingData->columns.size() - 1 && (TAPE_DIR == SCROLL_DIR_LEFT || TAPE_DIR == SCROLL_DIR_UP))
return false; return;
DATA->column->remove(t); DATA->column->remove(t);
@ -943,34 +884,15 @@ void CScrollingAlgorithm::moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool
m_scrollingData->centerOrFitCol(COL); m_scrollingData->centerOrFitCol(COL);
} }
return true; } else if (dir == Math::DIRECTION_UP)
} else if (ROTATED_DIR == Math::DIRECTION_UP) DATA->column->up(DATA);
return DATA->column->up(DATA); else if (dir == Math::DIRECTION_DOWN)
else if (ROTATED_DIR == Math::DIRECTION_DOWN) DATA->column->down(DATA);
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();
return;
}
}
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) {
@ -1257,7 +1179,6 @@ std::expected<void, std::string> CScrollingAlgorithm::layoutMsg(const std::strin
} 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);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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;
@ -1467,7 +1470,6 @@ 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) {
@ -1477,7 +1479,6 @@ 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);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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;
} }
@ -384,10 +380,10 @@ bool CScreenshareFrame::copyShm() {
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;
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,13 +87,6 @@ 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);
if (!PSHMINFO) {
LOGM(Log::ERR, "No pixel format for drm format");
m_resource->sendFailed();
return;
}
const auto stride = NFormatUtils::minStride(PSHMINFO, bufSize.x); 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);
@ -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");

View file

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

View file

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

View file

@ -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");

View file

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

View file

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

View file

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