diff --git a/.github/actions/setup_base/action.yml b/.github/actions/setup_base/action.yml index 665d7f07db2..e945e56408d 100644 --- a/.github/actions/setup_base/action.yml +++ b/.github/actions/setup_base/action.yml @@ -24,6 +24,7 @@ runs: glm \ glslang \ go \ + gtest \ hyprlang \ hyprcursor \ jq \ diff --git a/.github/workflows/nix-ci.yml b/.github/workflows/nix-ci.yml index ae615057873..5b22e99280a 100644 --- a/.github/workflows/nix-ci.yml +++ b/.github/workflows/nix-ci.yml @@ -25,6 +25,5 @@ jobs: test: if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork) - needs: hyprland uses: ./.github/workflows/nix-test.yml secrets: inherit diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d47b57ff85..61ecb352801 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,9 +80,11 @@ message( if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) message(STATUS "Configuring Hyprland in Debug with CMake") add_compile_definitions(HYPRLAND_DEBUG) + set(BUILD_TESTING ON) else() add_compile_options(-O3) message(STATUS "Configuring Hyprland in Release with CMake") + set(BUILD_TESTING OFF) endif() add_compile_definitions(HYPRLAND_VERSION="${HYPRLAND_VERSION}") @@ -231,7 +233,7 @@ set_source_files_properties(${CMAKE_SOURCE_DIR}/src/version.h PROPERTIES GENERAT pkg_check_modules( deps REQUIRED - IMPORTED_TARGET + IMPORTED_TARGET GLOBAL xkbcommon>=1.11.0 uuid wayland-server>=1.22.90 @@ -251,6 +253,8 @@ pkg_check_modules( find_package(hyprwayland-scanner 0.3.10 REQUIRED) file(GLOB_RECURSE SRCFILES "src/*.cpp") +get_filename_component(FULL_MAIN_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp ABSOLUTE) +list(REMOVE_ITEM SRCFILES "${FULL_MAIN_PATH}") set(TRACY_CPP_FILES "") if(USE_TRACY) @@ -258,7 +262,12 @@ if(USE_TRACY) message(STATUS "Tracy enabled, TRACY_CPP_FILES: " ${TRACY_CPP_FILES}) endif() -add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES}) +add_library(hyprland_lib STATIC ${SRCFILES}) +add_executable(Hyprland src/main.cpp ${TRACY_CPP_FILES}) +target_link_libraries(Hyprland hyprland_lib) + +target_include_directories(hyprland_lib PUBLIC ${deps_INCLUDE_DIRS}) +target_include_directories(Hyprland PUBLIC ${deps_INCLUDE_DIRS}) set(USE_GPROF OFF) @@ -268,8 +277,8 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) if(WITH_ASAN) message(STATUS "Enabling ASan") - target_link_libraries(Hyprland asan) - target_compile_options(Hyprland PUBLIC -fsanitize=address) + target_link_libraries(hyprland_lib PUBLIC asan) + target_compile_options(hyprland_lib PUBLIC -fsanitize=address) endif() if(USE_TRACY) @@ -279,7 +288,7 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) option(TRACY_ON_DEMAND "" ON) add_subdirectory(subprojects/tracy) - target_link_libraries(Hyprland Tracy::TracyClient) + target_link_libraries(hyprland_lib PUBLIC Tracy::TracyClient) if(USE_TRACY_GPU) message(STATUS "Tracy GPU Profiling is turned on") @@ -304,19 +313,19 @@ endif() include(CheckLibraryExists) check_library_exists(execinfo backtrace "" HAVE_LIBEXECINFO) if(HAVE_LIBEXECINFO) - target_link_libraries(Hyprland execinfo) + target_link_libraries(hyprland_lib PUBLIC execinfo) endif() check_include_file("sys/timerfd.h" HAS_TIMERFD) pkg_check_modules(epoll IMPORTED_TARGET epoll-shim) if(NOT HAS_TIMERFD AND epoll_FOUND) - target_link_libraries(Hyprland PkgConfig::epoll) + target_link_libraries(hyprland_lib PUBLIC PkgConfig::epoll) endif() check_include_file("sys/inotify.h" HAS_INOTIFY) pkg_check_modules(inotify IMPORTED_TARGET libinotify) if(NOT HAS_INOTIFY AND inotify_FOUND) - target_link_libraries(Hyprland PkgConfig::inotify) + target_link_libraries(hyprland_lib PUBLIC PkgConfig::inotify) endif() if(NO_XWAYLAND) @@ -335,7 +344,7 @@ else() xcb-composite xcb-res xcb-errors) - target_link_libraries(Hyprland PkgConfig::xdeps) + target_link_libraries(hyprland_lib PUBLIC PkgConfig::xdeps) endif() if(NO_SYSTEMD) @@ -362,31 +371,38 @@ if(CMAKE_DISABLE_PRECOMPILE_HEADERS) message(STATUS "Not using precompiled headers") else() message(STATUS "Setting precompiled headers") - target_precompile_headers(Hyprland PRIVATE + target_precompile_headers(hyprland_lib PRIVATE $<$:src/pch/pch.hpp>) endif() message(STATUS "Setting link libraries") target_link_libraries( - Hyprland - ${LIBRT} + hyprland_lib + PUBLIC PkgConfig::aquamarine_dep PkgConfig::hyprlang_dep PkgConfig::hyprutils_dep PkgConfig::hyprcursor_dep PkgConfig::hyprgraphics_dep - PkgConfig::deps) + PkgConfig::deps +) + +target_link_libraries( + Hyprland + ${LIBRT} + hyprland_lib) if(udis_dep_FOUND) - target_link_libraries(Hyprland PkgConfig::udis_dep) + target_link_libraries(hyprland_lib PUBLIC PkgConfig::udis_dep) elseif(NOT("${udis_nopc}" MATCHES "udis_nopc-NOTFOUND")) - target_link_libraries(Hyprland ${udis_nopc}) + target_link_libraries(hyprland_lib PUBLIC ${udis_nopc}) else() - target_link_libraries(Hyprland libudis86) + target_link_libraries(hyprland_lib PUBLIC libudis86) endif() # used by `make installheaders`, to ensure the headers are generated add_custom_target(generate-protocol-headers) +set(PROTOCOL_SOURCES "") function(protocolnew protoPath protoName external) if(external) @@ -400,10 +416,15 @@ function(protocolnew protoPath protoName external) COMMAND hyprwayland-scanner ${path}/${protoName}.xml ${CMAKE_SOURCE_DIR}/protocols/ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - target_sources(Hyprland PRIVATE protocols/${protoName}.cpp + target_sources(hyprland_lib PRIVATE protocols/${protoName}.cpp protocols/${protoName}.hpp) target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp) + + list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp") + set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE) + list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp") + set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE) endfunction() function(protocolWayland) add_custom_command( @@ -413,12 +434,17 @@ function(protocolWayland) hyprwayland-scanner --wayland-enums ${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - target_sources(Hyprland PRIVATE protocols/wayland.cpp protocols/wayland.hpp) + target_sources(hyprland_lib PRIVATE protocols/wayland.cpp protocols/wayland.hpp) target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp) + + list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/wayland.hpp") + set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE) + list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/wayland.cpp") + set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE) endfunction() -target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads) +target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL Threads::Threads) pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4) if(hyprland_protocols_dep_FOUND) @@ -563,10 +589,37 @@ install( PATTERN "*.hpp" PATTERN "*.inc") -if(BUILD_TESTING OR BUILD_HYPRTESTER) - message(STATUS "Building hyprtester") +if(BUILD_TESTING OR WITH_TESTS) + message(STATUS "Building tests") + # hyprtester add_subdirectory(hyprtester) + + # GTest + find_package(GTest CONFIG REQUIRED) + include(GoogleTest) + file(GLOB_RECURSE TESTFILES "tests/*.cpp") + add_executable(hyprland_gtests ${TESTFILES}) + target_compile_options(hyprland_gtests PRIVATE --coverage) + target_link_options(hyprland_gtests PRIVATE --coverage) + target_include_directories( + hyprland_gtests + PUBLIC "./include" + PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}") + + target_link_libraries(hyprland_gtests hyprland_lib GTest::gtest_main) + + gtest_discover_tests(hyprland_gtests) + + # Enable coverage in main hyprland lib + target_compile_options(hyprland_lib PRIVATE --coverage) + target_link_options(hyprland_lib PRIVATE --coverage) + target_link_libraries(hyprland_lib PUBLIC gcov) + + # Enable coverage in hyprland exe + target_compile_options(Hyprland PRIVATE --coverage) + target_link_options(Hyprland PRIVATE --coverage) + target_link_libraries(Hyprland gcov) endif() if(BUILD_TESTING) @@ -575,12 +628,8 @@ if(BUILD_TESTING) enable_testing() add_custom_target(tests) - add_test( - NAME "Main Test" - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/hyprtester - COMMAND hyprtester) + add_dependencies(tests hyprland_gtests) - add_dependencies(tests hyprtester) else() message(STATUS "Testing is disabled") endif() diff --git a/flake.lock b/flake.lock index a20ecfed793..8da31172149 100644 --- a/flake.lock +++ b/flake.lock @@ -16,11 +16,11 @@ ] }, "locked": { - "lastModified": 1763922789, - "narHash": "sha256-XnkWjCpeXfip9tqYdL0b0zzBDjq+dgdISvEdSVGdVyA=", + "lastModified": 1764370710, + "narHash": "sha256-7iZklFmziy6Vn5ZFy9mvTSuFopp3kJNuPxL5QAvtmFQ=", "owner": "hyprwm", "repo": "aquamarine", - "rev": "a20a0e67a33b6848378a91b871b89588d3a12573", + "rev": "561ae7fbe1ca15dfd908262ec815bf21a13eef63", "type": "github" }, "original": { @@ -144,11 +144,11 @@ ] }, "locked": { - "lastModified": 1763727565, - "narHash": "sha256-vRff/2R1U1jzPBy4OODqh2kfUzmizW/nfV2ROzTDIKo=", + "lastModified": 1764422822, + "narHash": "sha256-K4J8yEfFOX4bcYxutUVPr8aZtCSOjFI+RwbAiKIr1EI=", "owner": "hyprwm", "repo": "hyprland-guiutils", - "rev": "7724d3a12a0453e7aae05f2ef39474219f05a4b4", + "rev": "e83430c137bb01346cdd0d91ced6b05fb1d565d6", "type": "github" }, "original": { @@ -193,11 +193,11 @@ ] }, "locked": { - "lastModified": 1763819661, - "narHash": "sha256-0jLarTR/BLWdGlboM86bPVP2zKJNI2jvo3JietnDkOM=", + "lastModified": 1764348761, + "narHash": "sha256-P3ccZfKRLyo3Sc4+xdK3418vMvATw/BhdujJtRVsDQw=", "owner": "hyprwm", "repo": "hyprlang", - "rev": "a318deec0c12409ec39c68d2be8096b636dc2a5c", + "rev": "eb5be96aa0e401458dca3f6bc9fd3b94553efc01", "type": "github" }, "original": { @@ -238,11 +238,11 @@ ] }, "locked": { - "lastModified": 1763503177, - "narHash": "sha256-VPoiswJBBmTLVuNncvT/8FpFR+sYcAi/LgP/zTZ+5rA=", + "lastModified": 1764282573, + "narHash": "sha256-hbd5QccWWkzo+3FJzT8lxIKrEZM7naCRuMO7/hcBqWU=", "owner": "hyprwm", "repo": "hyprtoolkit", - "rev": "f4e1e12755567ecf39090203b8f43eace8279630", + "rev": "a8473c0e235822e20632202429cad8f30fa17c63", "type": "github" }, "original": { @@ -261,11 +261,11 @@ ] }, "locked": { - "lastModified": 1763996058, - "narHash": "sha256-DsqzFZvrEV+aDmavjaD4/bk5qxeZwhGxPWBQdpFyM9Y=", + "lastModified": 1764464068, + "narHash": "sha256-0TphQ03mCptteIXI7skQeRjwkl1bWGvNfGR020rWSKo=", "owner": "hyprwm", "repo": "hyprutils", - "rev": "0168583075baffa083032ed13a8bea8ea12f281a", + "rev": "7e6346f84be8918e3eca405546c45fb37d74bdfe", "type": "github" }, "original": { @@ -299,11 +299,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1763966396, - "narHash": "sha256-6eeL1YPcY1MV3DDStIDIdy/zZCDKgHdkCmsrLJFiZf0=", + "lastModified": 1764517877, + "narHash": "sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5ae3b07d8d6527c42f17c876e404993199144b6a", + "rev": "2d293cbfa5a793b4c50d17c05ef9e385b90edf6c", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 6799144b46c..af843d33daa 100644 --- a/flake.nix +++ b/flake.nix @@ -152,6 +152,7 @@ # hyprland-packages hyprland hyprland-unwrapped + hyprland-with-tests # hyprland-extras xdg-desktop-portal-hyprland ; diff --git a/hyprtester/src/tests/main/window.cpp b/hyprtester/src/tests/main/window.cpp index 3265bf72ca5..8d52a1051da 100644 --- a/hyprtester/src/tests/main/window.cpp +++ b/hyprtester/src/tests/main/window.cpp @@ -364,22 +364,40 @@ static bool test() { NLog::log("{}Testing window split ratios", Colors::YELLOW); { - const double RATIO = 1.25; - const double PERCENT = RATIO / 2.0 * 100.0; - const int GAPSIN = 5; - const int GAPSOUT = 20; - const int BORDERS = 2 * 2; - const int WTRIM = BORDERS + GAPSIN + GAPSOUT; - const int HEIGHT = 1080 - (BORDERS + (GAPSOUT * 2)); - const int WIDTH1 = std::round(1920.0 / 2.0 * (2 - RATIO)) - WTRIM; - const int WIDTH2 = std::round(1920.0 / 2.0 * RATIO) - WTRIM; + const double INITIAL_RATIO = 1.25; + const int GAPSIN = 5; + const int GAPSOUT = 20; + const int BORDERSIZE = 2; + const int BORDERS = BORDERSIZE * 2; + const int MONITOR_W = 1920; + const int MONITOR_H = 1080; + + const float totalAvailableHeight = MONITOR_H - (GAPSOUT * 2); + const int HEIGHT = std::floor(totalAvailableHeight) - BORDERS; + const float availableWidthForSplit = MONITOR_W - (GAPSOUT * 2) - GAPSIN; + + auto calculateFinalWidth = [&](double boxWidth, bool isLeftWindow) { + double gapLeft = isLeftWindow ? GAPSOUT : GAPSIN; + double gapRight = isLeftWindow ? GAPSIN : GAPSOUT; + return std::floor(boxWidth - gapLeft - gapRight - BORDERS); + }; + + double geomBoxWidthA_R1 = (availableWidthForSplit * INITIAL_RATIO / 2.0) + GAPSOUT + (GAPSIN / 2.0); + double geomBoxWidthB_R1 = MONITOR_W - geomBoxWidthA_R1; + const int WIDTH1 = calculateFinalWidth(geomBoxWidthB_R1, false); + + const double INVERTED_RATIO = 0.75; + double geomBoxWidthA_R2 = (availableWidthForSplit * INVERTED_RATIO / 2.0) + GAPSOUT + (GAPSIN / 2.0); + double geomBoxWidthB_R2 = MONITOR_W - geomBoxWidthA_R2; + const int WIDTH2 = calculateFinalWidth(geomBoxWidthB_R2, false); + const int WIDTH_A_FINAL = calculateFinalWidth(geomBoxWidthA_R2, true); OK(getFromSocket("/keyword dwindle:default_split_ratio 1.25")); if (!spawnKitty("kitty_B")) return false; - NLog::log("{}Expecting kitty_B to take up roughly {}% of screen width", Colors::YELLOW, 100 - PERCENT); + NLog::log("{}Expecting kitty_B size: {},{}", Colors::YELLOW, WIDTH1, HEIGHT); EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH1, HEIGHT)); OK(getFromSocket("/dispatch killwindow activewindow")); @@ -391,12 +409,12 @@ static bool test() { if (!spawnKitty("kitty_B")) return false; - NLog::log("{}Expecting kitty_B to take up roughly {}% of screen width", Colors::YELLOW, PERCENT); + NLog::log("{}Expecting kitty_B size: {},{}", Colors::YELLOW, WIDTH2, HEIGHT); EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH2, HEIGHT)); OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - NLog::log("{}Expecting kitty_A to have the same width as the previous kitty_B", Colors::YELLOW); - EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH1, HEIGHT)); + NLog::log("{}Expecting kitty_A size: {},{}", Colors::YELLOW, WIDTH_A_FINAL, HEIGHT); + EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH_A_FINAL, HEIGHT)); OK(getFromSocket("/keyword dwindle:default_split_ratio 1")); } diff --git a/hyprtester/src/tests/main/workspaces.cpp b/hyprtester/src/tests/main/workspaces.cpp index 622236dcfe3..c1b9690ae68 100644 --- a/hyprtester/src/tests/main/workspaces.cpp +++ b/hyprtester/src/tests/main/workspaces.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include "../shared.hpp" @@ -14,10 +15,99 @@ static int ret = 0; using namespace Hyprutils::OS; using namespace Hyprutils::Memory; +using namespace Hyprutils::Utils; #define UP CUniquePointer #define SP CSharedPointer +static bool testAsymmetricGaps() { + NLog::log("{}Testing asymmetric gap splits", Colors::YELLOW); + { + + CScopeGuard guard = {[&]() { + NLog::log("{}Cleaning up asymmetric gap test", Colors::YELLOW); + Tests::killAllWindows(); + OK(getFromSocket("/reload")); + }}; + + OK(getFromSocket("/dispatch workspace name:gap_split_test")); + OK(getFromSocket("r/keyword general:gaps_in 0")); + OK(getFromSocket("r/keyword general:border_size 0")); + OK(getFromSocket("r/keyword dwindle:split_width_multiplier 1.0")); + OK(getFromSocket("r/keyword workspace name:gap_split_test,gapsout:0 1000 0 0")); + + NLog::log("{}Testing default split (force_split = 0)", Colors::YELLOW); + OK(getFromSocket("r/keyword dwindle:force_split 0")); + + if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) + return false; + + NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW); + OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); + OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540"); + + Tests::killAllWindows(); + EXPECT(Tests::windowCount(), 0); + + NLog::log("{}Testing force_split = 1", Colors::YELLOW); + OK(getFromSocket("r/keyword dwindle:force_split 1")); + + if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) + return false; + + NLog::log("{}Expecting vertical split (B above A)", Colors::YELLOW); + OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); + OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540"); + + NLog::log("{}Expecting horizontal split (C left of B)", Colors::YELLOW); + OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B")); + + if (!Tests::spawnKitty("gaps_kitty_C")) + return false; + + OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_C")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); + OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0"); + + Tests::killAllWindows(); + EXPECT(Tests::windowCount(), 0); + + NLog::log("{}Testing force_split = 2", Colors::YELLOW); + OK(getFromSocket("r/keyword dwindle:force_split 2")); + + if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) + return false; + + NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW); + OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); + OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540"); + + NLog::log("{}Expecting horizontal split (C right of A)", Colors::YELLOW); + OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A")); + + if (!Tests::spawnKitty("gaps_kitty_C")) + return false; + + OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); + OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_C")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0"); + } + + // kill all + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + + return true; +} + static bool test() { NLog::log("{}Testing workspaces", Colors::GREEN); @@ -359,6 +449,8 @@ static bool test() { NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); + testAsymmetricGaps(); + NLog::log("{}Expecting 0 windows", Colors::YELLOW); EXPECT(Tests::windowCount(), 0); diff --git a/nix/default.nix b/nix/default.nix index 45fd273b853..272f35bba2e 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -12,6 +12,7 @@ epoll-shim, git, glaze, + gtest, hyprcursor, hyprgraphics, hyprland-protocols, @@ -39,6 +40,7 @@ xorg, xwayland, debug ? false, + withTests ? false, enableXWayland ? true, withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd, wrapRuntimeDeps ? true, @@ -74,7 +76,7 @@ in assert assertMsg (!withHyprtester) "The option `withHyprtester` has been removed. Hyprtester is always built now."; customStdenv.mkDerivation (finalAttrs: { pname = "hyprland${optionalString debug "-debug"}"; - inherit version; + inherit version withTests; src = fs.toSource { root = ../.; @@ -87,7 +89,6 @@ in ../assets/install ../hyprctl ../hyprland.pc.in - ../hyprtester ../LICENSE ../protocols ../src @@ -97,6 +98,7 @@ in (fs.fileFilter (file: file.hasExt "conf" || file.hasExt "desktop") ../example) (fs.fileFilter (file: file.hasExt "sh") ../scripts) (fs.fileFilter (file: file.name == "CMakeLists.txt") ../.) + (optional withTests [../tests ../hyprtester]) ])); }; @@ -139,6 +141,7 @@ in cairo git glaze + gtest hyprcursor hyprgraphics hyprland-protocols @@ -192,7 +195,7 @@ in "NO_UWSM" = true; "NO_HYPRPM" = true; "TRACY_ENABLE" = false; - "BUILD_HYPRTESTER" = true; + "WITH_TESTS" = withTests; }; preConfigure = '' @@ -212,8 +215,11 @@ in ]} ''} - install hyprtester/pointer-warp -t $out/bin - install hyprtester/pointer-scroll -t $out/bin + ${optionalString withTests '' + install hyprtester/pointer-warp -t $out/bin + install hyprtester/pointer-scroll -t $out/bin + install hyprland_gtests -t $out/bin + ''} ''; passthru.providedSessions = ["hyprland"]; diff --git a/nix/overlays.nix b/nix/overlays.nix index c7ef95b865e..94b35fd5cc5 100644 --- a/nix/overlays.nix +++ b/nix/overlays.nix @@ -43,6 +43,8 @@ in { }; hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;}; + hyprland-with-tests = final.hyprland.override {withTests = true;}; + hyprland-with-hyprtester = builtins.trace '' hyprland-with-hyprtester was removed. Please use the hyprland package. diff --git a/nix/tests/default.nix b/nix/tests/default.nix index ef92a463587..bdb3fe7c6ab 100644 --- a/nix/tests/default.nix +++ b/nix/tests/default.nix @@ -1,6 +1,6 @@ inputs: pkgs: let flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}; - hyprland = flake.hyprland; + hyprland = flake.hyprland-with-tests; in { tests = pkgs.testers.runNixOSTest { name = "hyprland-tests"; @@ -68,6 +68,12 @@ in { # Wait for tty to be up machine.wait_for_unit("multi-user.target") + + # Run gtests + print("Running gtests") + exit_status, _out = machine.execute("su - alice -c 'hyprland_gtests 2>&1 | tee /tmp/gtestslog; exit ''${PIPESTATUS[0]}'") + machine.execute(f'echo {exit_status} > /tmp/exit_status_gtests') + # Run hyprtester testing framework/suite print("Running hyprtester") exit_status, _out = machine.execute("su - alice -c 'hyprtester -b ${hyprland}/bin/Hyprland -c /etc/test.conf -p ${hyprland}/lib/hyprtestplugin.so 2>&1 | tee /tmp/testerlog; exit ''${PIPESTATUS[0]}'") @@ -76,6 +82,7 @@ in { # Copy logs to host machine.execute('cp "$(find /tmp/hypr -name *.log | head -1)" /tmp/hyprlog') machine.execute(f'echo {exit_status} > /tmp/exit_status') + machine.copy_from_vm("/tmp/gtestslog") machine.copy_from_vm("/tmp/testerlog") machine.copy_from_vm("/tmp/hyprlog") machine.copy_from_vm("/tmp/exit_status") diff --git a/src/Compositor.cpp b/src/Compositor.cpp index e8829fd084a..32ad07901c3 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -1602,10 +1602,13 @@ bool CCompositor::isPointOnAnyMonitor(const Vector2D& point) { bool CCompositor::isPointOnReservedArea(const Vector2D& point, const PHLMONITOR pMonitor) { const auto PMONITOR = pMonitor ? pMonitor : getMonitorFromVector(point); - const auto XY1 = PMONITOR->m_position + PMONITOR->m_reservedTopLeft; - const auto XY2 = PMONITOR->m_position + PMONITOR->m_size - PMONITOR->m_reservedBottomRight; + auto box = PMONITOR->logicalBox(); + if (VECNOTINRECT(point, box.x - 1, box.y - 1, box.w + 2, box.h + 2)) + return false; + + PMONITOR->m_reservedArea.applyip(box); - return VECNOTINRECT(point, XY1.x, XY1.y, XY2.x, XY2.y); + return VECNOTINRECT(point, box.x, box.y, box.x, box.y); } CBox CCompositor::calculateX11WorkArea() { @@ -1615,11 +1618,7 @@ CBox CCompositor::calculateX11WorkArea() { for (const auto& monitor : m_monitors) { // we ignore monitor->m_position on purpose - auto x = monitor->m_reservedTopLeft.x; - auto y = monitor->m_reservedTopLeft.y; - auto w = monitor->m_size.x - monitor->m_reservedBottomRight.x - x; - auto h = monitor->m_size.y - monitor->m_reservedBottomRight.y - y; - CBox box = {x, y, w, h}; + CBox box = monitor->logicalBoxMinusReserved().translate(-monitor->m_position); if ((*PXWLFORCESCALEZERO)) box.scale(monitor->m_scale); diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index b6b52d345bf..d3bfba9808d 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -1093,7 +1093,6 @@ std::optional CConfigManager::resetHLConfig() { g_pAnimationManager->addBezierWithName("linear", Vector2D(0.0, 0.0), Vector2D(1.0, 1.0)); g_pTrackpadGestures->clearGestures(); - m_mAdditionalReservedAreas.clear(); m_workspaceRules.clear(); setDefaultAnimationVars(); // reset anims m_declaredPlugins.clear(); @@ -1137,7 +1136,8 @@ std::optional CConfigManager::handleMonitorv2(const std::string& ou if (VAL && VAL->m_bSetByUser) { const auto ARGS = CVarList(std::any_cast(VAL->getValue())); try { - parser.setReserved({.top = std::stoi(ARGS[0]), .bottom = std::stoi(ARGS[1]), .left = std::stoi(ARGS[2]), .right = std::stoi(ARGS[3])}); + // top, right, bottom, left + parser.setReserved({std::stoi(ARGS[0]), std::stoi(ARGS[3]), std::stoi(ARGS[1]), std::stoi(ARGS[2])}); } catch (...) { return "parse error: invalid reserved area"; } } VAL = m_config->getSpecialConfigValuePtr("monitorv2", "mirror", output.c_str()); @@ -2191,8 +2191,8 @@ void CMonitorRuleParser::setMirror(const std::string& value) { m_rule.mirrorOf = value; } -bool CMonitorRuleParser::setReserved(const SMonitorAdditionalReservedArea& value) { - g_pConfigManager->m_mAdditionalReservedAreas[name()] = value; +bool CMonitorRuleParser::setReserved(const Desktop::CReservedArea& value) { + m_rule.reservedArea = value; return true; } @@ -2221,13 +2221,22 @@ std::optional CConfigManager::handleMonitor(const std::string& comm return {}; } else if (ARGS[1] == "addreserved") { + std::optional area; try { - parser.setReserved({.top = std::stoi(std::string(ARGS[2])), - .bottom = std::stoi(std::string(ARGS[3])), - .left = std::stoi(std::string(ARGS[4])), - .right = std::stoi(std::string(ARGS[5]))}); + // top, right, bottom, left + area = {std::stoi(std::string{ARGS[2]}), std::stoi(std::string{ARGS[5]}), std::stoi(std::string{ARGS[3]}), std::stoi(std::string{ARGS[4]})}; } catch (...) { return "parse error: invalid reserved area"; } - return {}; + + if (!area.has_value()) + return "parse error: bad addreserved"; + + auto rule = std::ranges::find_if(m_monitorRules, [n = ARGS[0]](const auto& other) { return other.name == n; }); + if (rule != m_monitorRules.end()) { + rule->reservedArea = area.value(); + return {}; + } + + // fall } else { Debug::log(ERR, "ConfigManager parseMonitor, curitem bogus???"); return "parse error: curitem bogus"; diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index 599ee8e71c3..a13547b576d 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -18,6 +18,7 @@ #include "../SharedDefs.hpp" #include "../helpers/Color.hpp" #include "../desktop/DesktopTypes.hpp" +#include "../desktop/reserved/ReservedArea.hpp" #include "../helpers/memory/Memory.hpp" #include "../managers/XWaylandManager.hpp" #include "../managers/KeybindManager.hpp" @@ -48,13 +49,6 @@ struct SWorkspaceRule { std::map layoutopts; }; -struct SMonitorAdditionalReservedArea { - int top = 0; - int bottom = 0; - int left = 0; - int right = 0; -}; - struct SPluginKeyword { HANDLE handle = nullptr; std::string name = ""; @@ -185,7 +179,7 @@ class CMonitorRuleParser { void setDisabled(); void setMirror(const std::string& value); - bool setReserved(const SMonitorAdditionalReservedArea& value); + bool setReserved(const Desktop::CReservedArea& value); private: SMonitorRule m_rule; @@ -196,36 +190,34 @@ class CConfigManager { public: CConfigManager(); - void init(); - void reload(); - std::string verify(); - - int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = ""); - float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = ""); - Vector2D getDeviceVec(const std::string&, const std::string&, const std::string& fallback = ""); - std::string getDeviceString(const std::string&, const std::string&, const std::string& fallback = ""); - bool deviceConfigExplicitlySet(const std::string&, const std::string&); - bool deviceConfigExists(const std::string&); - Hyprlang::CConfigValue* getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback); + void init(); + void reload(); + std::string verify(); - void* const* getConfigValuePtr(const std::string&); - Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = ""); - std::string getMainConfigPath(); - std::string getConfigString(); + int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = ""); + float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = ""); + Vector2D getDeviceVec(const std::string&, const std::string&, const std::string& fallback = ""); + std::string getDeviceString(const std::string&, const std::string&, const std::string& fallback = ""); + bool deviceConfigExplicitlySet(const std::string&, const std::string&); + bool deviceConfigExists(const std::string&); + Hyprlang::CConfigValue* getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback); - SMonitorRule getMonitorRuleFor(const PHLMONITOR); - SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace); - std::string getDefaultWorkspaceFor(const std::string&); + void* const* getConfigValuePtr(const std::string&); + Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = ""); + std::string getMainConfigPath(); + std::string getConfigString(); - PHLMONITOR getBoundMonitorForWS(const std::string&); - std::string getBoundMonitorStringForWS(const std::string&); - const std::vector& getAllWorkspaceRules(); + SMonitorRule getMonitorRuleFor(const PHLMONITOR); + SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace); + std::string getDefaultWorkspaceFor(const std::string&); - void ensurePersistentWorkspacesPresent(); + PHLMONITOR getBoundMonitorForWS(const std::string&); + std::string getBoundMonitorStringForWS(const std::string&); + const std::vector& getAllWorkspaceRules(); - const std::vector& getAllDescriptions(); + void ensurePersistentWorkspacesPresent(); - std::unordered_map m_mAdditionalReservedAreas; + const std::vector& getAllDescriptions(); const std::unordered_map>& getAnimationConfig(); diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index dc5be6ce957..414d6182ab1 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -255,8 +255,8 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer escapeJSONStrings(m->m_output->serial), sc(m->m_pixelSize.x), sc(m->m_pixelSize.y), sc(m->m_output->physicalSize.x), sc(m->m_output->physicalSize.y), m->m_refreshRate, sc(m->m_position.x), sc(m->m_position.y), m->activeWorkspaceID(), (!m->m_activeWorkspace ? "" : escapeJSONStrings(m->m_activeWorkspace->m_name)), m->activeSpecialWorkspaceID(), - escapeJSONStrings(m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""), sc(m->m_reservedTopLeft.x), sc(m->m_reservedTopLeft.y), - sc(m->m_reservedBottomRight.x), sc(m->m_reservedBottomRight.y), m->m_scale, sc(m->m_transform), + escapeJSONStrings(m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""), sc(m->m_reservedArea.left()), sc(m->m_reservedArea.top()), + sc(m->m_reservedArea.right()), sc(m->m_reservedArea.bottom()), m->m_scale, sc(m->m_transform), (m == Desktop::focusState()->monitor() ? "true" : "false"), (m->m_dpmsStatus ? "true" : "false"), (m->m_output->state->state().adaptiveSync ? "true" : "false"), rc(m->m_solitaryClient.get()), getSolitaryBlockedReason(m, format), (m->m_tearingState.activelyTearing ? "true" : "false"), getTearingBlockedReason(m, format), rc(m->m_lastScanout.get()), getDSBlockedReason(m, format), (m->m_enabled ? "false" : "true"), @@ -274,7 +274,7 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer m->m_name, m->m_id, sc(m->m_pixelSize.x), sc(m->m_pixelSize.y), m->m_refreshRate, sc(m->m_position.x), sc(m->m_position.y), m->m_shortDescription, m->m_output->make, m->m_output->model, sc(m->m_output->physicalSize.x), sc(m->m_output->physicalSize.y), m->m_output->serial, m->activeWorkspaceID(), (!m->m_activeWorkspace ? "" : m->m_activeWorkspace->m_name), m->activeSpecialWorkspaceID(), (m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""), - sc(m->m_reservedTopLeft.x), sc(m->m_reservedTopLeft.y), sc(m->m_reservedBottomRight.x), sc(m->m_reservedBottomRight.y), m->m_scale, + sc(m->m_reservedArea.left()), sc(m->m_reservedArea.top()), sc(m->m_reservedArea.right()), sc(m->m_reservedArea.bottom()), m->m_scale, sc(m->m_transform), (m == Desktop::focusState()->monitor() ? "yes" : "no"), sc(m->m_dpmsStatus), m->m_output->state->state().adaptiveSync, rc(m->m_solitaryClient.get()), getSolitaryBlockedReason(m, format), m->m_tearingState.activelyTearing, getTearingBlockedReason(m, format), rc(m->m_lastScanout.get()), getDSBlockedReason(m, format), !m->m_enabled, formatToString(m->m_output->state->state().drmFormat), diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index 8a0e37a6c03..75dc7215321 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -229,19 +229,19 @@ CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() { return CBox{sc(POS.x), sc(POS.y), sc(SIZE.x), sc(SIZE.y)}; } - if (DELTALESSTHAN(POS.y - PMONITOR->m_position.y, PMONITOR->m_reservedTopLeft.y, 1)) { + if (DELTALESSTHAN(POS.y - PMONITOR->m_position.y, PMONITOR->m_reservedArea.top(), 1)) { POS.y = PMONITOR->m_position.y; - SIZE.y += PMONITOR->m_reservedTopLeft.y; + SIZE.y += PMONITOR->m_reservedArea.top(); } - if (DELTALESSTHAN(POS.x - PMONITOR->m_position.x, PMONITOR->m_reservedTopLeft.x, 1)) { + if (DELTALESSTHAN(POS.x - PMONITOR->m_position.x, PMONITOR->m_reservedArea.left(), 1)) { POS.x = PMONITOR->m_position.x; - SIZE.x += PMONITOR->m_reservedTopLeft.x; + SIZE.x += PMONITOR->m_reservedArea.left(); } - if (DELTALESSTHAN(POS.x + SIZE.x - PMONITOR->m_position.x, PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x, 1)) { - SIZE.x += PMONITOR->m_reservedBottomRight.x; + if (DELTALESSTHAN(POS.x + SIZE.x - PMONITOR->m_position.x, PMONITOR->m_size.x - PMONITOR->m_reservedArea.right(), 1)) { + SIZE.x += PMONITOR->m_reservedArea.right(); } - if (DELTALESSTHAN(POS.y + SIZE.y - PMONITOR->m_position.y, PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y, 1)) { - SIZE.y += PMONITOR->m_reservedBottomRight.y; + if (DELTALESSTHAN(POS.y + SIZE.y - PMONITOR->m_position.y, PMONITOR->m_size.y - PMONITOR->m_reservedArea.bottom(), 1)) { + SIZE.y += PMONITOR->m_reservedArea.bottom(); } return CBox{sc(POS.x), sc(POS.y), sc(SIZE.x), sc(SIZE.y)}; diff --git a/src/desktop/reserved/ReservedArea.cpp b/src/desktop/reserved/ReservedArea.cpp new file mode 100644 index 00000000000..b67524ce148 --- /dev/null +++ b/src/desktop/reserved/ReservedArea.cpp @@ -0,0 +1,89 @@ +#include "ReservedArea.hpp" +#include "../../macros.hpp" + +using namespace Desktop; + +// fuck me. Writing this at 11pm, and I have an in-class test tomorrow. +// I am failing that bitch + +CReservedArea::CReservedArea(const Vector2D& tl, const Vector2D& br) : m_initialTopLeft(tl), m_initialBottomRight(br) { + calculate(); +} + +CReservedArea::CReservedArea(double top, double right, double bottom, double left) : m_initialTopLeft(left, top), m_initialBottomRight(right, bottom) { + calculate(); +} + +CReservedArea::CReservedArea(const CBox& parent, const CBox& child) { + ASSERT(!parent.empty() && !child.empty()); + + ASSERT(parent.containsPoint(child.pos() + Vector2D{0.0001, 0.0001})); + ASSERT(parent.containsPoint(child.pos() + child.size() - Vector2D{0.0001, 0.0001})); + + m_initialTopLeft = child.pos() - parent.pos(); + m_initialBottomRight = (parent.pos() + parent.size()) - (child.pos() + child.size()); + + calculate(); +} + +void CReservedArea::calculate() { + m_bottomRight = m_initialBottomRight; + m_topLeft = m_initialTopLeft; + + for (const auto& e : m_dynamicReserved) { + m_bottomRight += e.bottomRight; + m_topLeft += e.topLeft; + } +} + +CBox CReservedArea::apply(const CBox& other) const { + auto c = other.copy(); + c.x += m_topLeft.x; + c.y += m_topLeft.y; + c.w -= m_topLeft.x + m_bottomRight.x; + c.h -= m_topLeft.y + m_bottomRight.y; + return c; +} + +void CReservedArea::applyip(CBox& other) const { + other.x += m_topLeft.x; + other.y += m_topLeft.y; + other.w -= m_topLeft.x + m_bottomRight.x; + other.h -= m_topLeft.y + m_bottomRight.y; +} + +bool CReservedArea::operator==(const CReservedArea& other) const { + return other.m_bottomRight == m_bottomRight && other.m_topLeft == m_topLeft; +} + +double CReservedArea::left() const { + return m_topLeft.x; +} + +double CReservedArea::right() const { + return m_bottomRight.x; +} + +double CReservedArea::top() const { + return m_topLeft.y; +} + +double CReservedArea::bottom() const { + return m_bottomRight.y; +} + +void CReservedArea::resetType(eReservedDynamicType t) { + m_dynamicReserved[t] = {}; + calculate(); +} + +void CReservedArea::addType(eReservedDynamicType t, const Vector2D& topLeft, const Vector2D& bottomRight) { + auto& ref = m_dynamicReserved[t]; + ref.topLeft += topLeft; + ref.bottomRight += bottomRight; + calculate(); +} + +void CReservedArea::addType(eReservedDynamicType t, const CReservedArea& area) { + addType(t, {area.left(), area.top()}, {area.right(), area.bottom()}); +} diff --git a/src/desktop/reserved/ReservedArea.hpp b/src/desktop/reserved/ReservedArea.hpp new file mode 100644 index 00000000000..2aca595d066 --- /dev/null +++ b/src/desktop/reserved/ReservedArea.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "../../helpers/math/Math.hpp" +#include + +namespace Desktop { + enum eReservedDynamicType : uint8_t { + RESERVED_DYNAMIC_TYPE_LS = 0, + RESERVED_DYNAMIC_TYPE_ERROR_BAR, + + RESERVED_DYNAMIC_TYPE_END, + }; + + class CReservedArea { + public: + CReservedArea() = default; + CReservedArea(const Vector2D& tl, const Vector2D& br); + CReservedArea(double top, double right, double bottom, double left); + CReservedArea(const CBox& parent, const CBox& child); + ~CReservedArea() = default; + + CBox apply(const CBox& other) const; + void applyip(CBox& other) const; + + void resetType(eReservedDynamicType); + void addType(eReservedDynamicType, const Vector2D& topLeft, const Vector2D& bottomRight); + void addType(eReservedDynamicType, const CReservedArea& area); + + double left() const; + double right() const; + double top() const; + double bottom() const; + + bool operator==(const CReservedArea& other) const; + + private: + void calculate(); + + Vector2D m_topLeft, m_bottomRight; + Vector2D m_initialTopLeft, m_initialBottomRight; + + struct SDynamicData { + Vector2D topLeft, bottomRight; + }; + + std::array m_dynamicReserved; + }; +}; \ No newline at end of file diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index 9a1c07d95d2..6b9ba1b9b3d 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -396,8 +396,8 @@ void Events::listener_mapWindow(void* owner, void* data) { } if (PWINDOW->m_ruleApplicator->static_.center) { - auto RESERVEDOFFSET = (PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight) / 2.f; - *PWINDOW->m_realPosition = PMONITOR->middle() - PWINDOW->m_realSize->goal() / 2.f + RESERVEDOFFSET; + const auto WORKAREA = PMONITOR->logicalBoxMinusReserved(); + *PWINDOW->m_realPosition = WORKAREA.middle() - PWINDOW->m_realSize->goal() / 2.f; } // set the pseudo size to the GOAL of our current size diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index abf74b02345..52fa659c56f 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -603,7 +603,7 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { && m_transform == RULE->transform && RULE->enable10bit == m_enabled10bit && RULE->cmType == m_cmType && RULE->sdrSaturation == m_sdrSaturation && RULE->sdrBrightness == m_sdrBrightness && RULE->sdrMinLuminance == m_minLuminance && RULE->sdrMaxLuminance == m_maxLuminance && RULE->supportsWideColor == m_supportsWideColor && RULE->supportsHDR == m_supportsHDR && RULE->minLuminance == m_minLuminance && RULE->maxLuminance == m_maxLuminance && - RULE->maxAvgLuminance == m_maxAvgLuminance && !std::memcmp(&m_customDrmMode, &RULE->drmMode, sizeof(m_customDrmMode))) { + RULE->maxAvgLuminance == m_maxAvgLuminance && !std::memcmp(&m_customDrmMode, &RULE->drmMode, sizeof(m_customDrmMode)) && m_reservedArea == RULE->reservedArea) { Debug::log(LOG, "Not applying a new rule to {} because it's already applied!", m_name); @@ -614,17 +614,18 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { bool autoScale = false; - if (RULE->scale > 0.1) { + if (RULE->scale > 0.1) m_scale = RULE->scale; - } else { + else { autoScale = true; const auto DEFAULTSCALE = getDefaultScale(); m_scale = DEFAULTSCALE; } - m_setScale = m_scale; - m_transform = RULE->transform; - m_autoDir = RULE->autoDir; + m_setScale = m_scale; + m_transform = RULE->transform; + m_autoDir = RULE->autoDir; + m_reservedArea = RULE->reservedArea; // accumulate requested modes in reverse order (cause inesrting at front is inefficient) std::vector> requestedModes; @@ -1517,8 +1518,8 @@ CBox CMonitor::logicalBox() { return {m_position, m_size}; } -CBox CMonitor::logicalBoxMinusExtents() { - return {m_position + m_reservedTopLeft, m_size - m_reservedTopLeft - m_reservedBottomRight}; +CBox CMonitor::logicalBoxMinusReserved() { + return m_reservedArea.apply(logicalBox()); } void CMonitor::scheduleDone() { diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index f1f46669813..94c8cc15e08 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -13,6 +13,7 @@ #include #include "time/Timer.hpp" #include "math/Math.hpp" +#include "../desktop/reserved/ReservedArea.hpp" #include #include "../protocols/types/ColorManagement.hpp" #include "signal/Signal.hpp" @@ -37,25 +38,26 @@ enum eAutoDirs : uint8_t { }; struct SMonitorRule { - eAutoDirs autoDir = DIR_AUTO_NONE; - std::string name = ""; - Vector2D resolution = Vector2D(1280, 720); - Vector2D offset = Vector2D(0, 0); - float scale = 1; - float refreshRate = 60; // Hz - bool disabled = false; - wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; - std::string mirrorOf = ""; - bool enable10bit = false; - NCMType::eCMType cmType = NCMType::CM_SRGB; - int sdrEotf = 0; - float sdrSaturation = 1.0f; // SDR -> HDR - float sdrBrightness = 1.0f; // SDR -> HDR - - bool supportsWideColor = false; // false does nothing, true overrides EDID - bool supportsHDR = false; // false does nothing, true overrides EDID - float sdrMinLuminance = 0.2f; // SDR -> HDR - int sdrMaxLuminance = 80; // SDR -> HDR + eAutoDirs autoDir = DIR_AUTO_NONE; + std::string name = ""; + Vector2D resolution = Vector2D(1280, 720); + Vector2D offset = Vector2D(0, 0); + float scale = 1; + float refreshRate = 60; // Hz + bool disabled = false; + wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + std::string mirrorOf = ""; + bool enable10bit = false; + NCMType::eCMType cmType = NCMType::CM_SRGB; + int sdrEotf = 0; + float sdrSaturation = 1.0f; // SDR -> HDR + float sdrBrightness = 1.0f; // SDR -> HDR + Desktop::CReservedArea reservedArea; + + bool supportsWideColor = false; // false does nothing, true overrides EDID + bool supportsHDR = false; // false does nothing, true overrides EDID + float sdrMinLuminance = 0.2f; // SDR -> HDR + int sdrMaxLuminance = 80; // SDR -> HDR // Incorrect values will result in reduced luminance range or incorrect tonemapping. Shouldn't damage the HW. Use with care in case of a faulty monitor firmware. float minLuminance = -1.0f; // >= 0 overrides EDID @@ -108,11 +110,10 @@ class CMonitor { std::string m_description = ""; std::string m_shortDescription = ""; - Vector2D m_reservedTopLeft = Vector2D(0, 0); - Vector2D m_reservedBottomRight = Vector2D(0, 0); - drmModeModeInfo m_customDrmMode = {}; + Desktop::CReservedArea m_reservedArea; + CMonitorState m_state; CDamageRing m_damage; @@ -298,7 +299,7 @@ class CMonitor { WORKSPACEID activeWorkspaceID(); WORKSPACEID activeSpecialWorkspaceID(); CBox logicalBox(); - CBox logicalBoxMinusExtents(); + CBox logicalBoxMinusReserved(); void scheduleDone(); uint32_t isSolitaryBlocked(bool full = false); void recheckSolitary(); diff --git a/src/hyprerror/HyprError.cpp b/src/hyprerror/HyprError.cpp index 3446fc4bdeb..c8125e5b9ce 100644 --- a/src/hyprerror/HyprError.cpp +++ b/src/hyprerror/HyprError.cpp @@ -162,7 +162,15 @@ void CHyprError::createQueued() { g_pHyprRenderer->damageMonitor(PMONITOR); - g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id); + for (const auto& m : g_pCompositor->m_monitors) { + m->m_reservedArea.resetType(Desktop::RESERVED_DYNAMIC_TYPE_ERROR_BAR); + } + + PMONITOR->m_reservedArea.addType(Desktop::RESERVED_DYNAMIC_TYPE_ERROR_BAR, Vector2D{0.0, *BAR_POSITION == 0 ? HEIGHT : 0.0}, Vector2D{0.0, *BAR_POSITION != 0 ? HEIGHT : 0.0}); + + for (const auto& m : g_pCompositor->m_monitors) { + g_pHyprRenderer->arrangeLayersForMonitor(m->m_id); + } } void CHyprError::draw() { diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp index cf833fb5389..a268d857225 100644 --- a/src/layout/DwindleLayout.cpp +++ b/src/layout/DwindleLayout.cpp @@ -41,35 +41,39 @@ void SDwindleNodeData::recalcSizePosRecursive(bool force, bool horizontalOverrid children[0]->recalcSizePosRecursive(force); children[1]->recalcSizePosRecursive(force); } else { - layout->applyNodeDataToWindow(this, force); + layout->applyNodeDataToWindow(self.lock(), force); } } +void SDwindleNodeData::applyRootBox() { + box = layout->workAreaOnWorkspace(g_pCompositor->getWorkspaceByID(workspaceID)); +} + int CHyprDwindleLayout::getNodesOnWorkspace(const WORKSPACEID& id) { int no = 0; for (auto const& n : m_dwindleNodesData) { - if (n.workspaceID == id && n.valid) + if (n->workspaceID == id && n->valid) ++no; } return no; } -SDwindleNodeData* CHyprDwindleLayout::getFirstNodeOnWorkspace(const WORKSPACEID& id) { +SP CHyprDwindleLayout::getFirstNodeOnWorkspace(const WORKSPACEID& id) { for (auto& n : m_dwindleNodesData) { - if (n.workspaceID == id && validMapped(n.pWindow)) - return &n; + if (n->workspaceID == id && validMapped(n->pWindow)) + return n; } return nullptr; } -SDwindleNodeData* CHyprDwindleLayout::getClosestNodeOnWorkspace(const WORKSPACEID& id, const Vector2D& point) { - SDwindleNodeData* res = nullptr; - double distClosest = -1; +SP CHyprDwindleLayout::getClosestNodeOnWorkspace(const WORKSPACEID& id, const Vector2D& point) { + SP res = nullptr; + double distClosest = -1; for (auto& n : m_dwindleNodesData) { - if (n.workspaceID == id && validMapped(n.pWindow)) { - auto distAnother = vecToRectDistanceSquared(point, n.box.pos(), n.box.pos() + n.box.size()); + if (n->workspaceID == id && validMapped(n->pWindow)) { + auto distAnother = vecToRectDistanceSquared(point, n->box.pos(), n->box.pos() + n->box.size()); if (!res || distAnother < distClosest) { - res = &n; + res = n; distClosest = distAnother; } } @@ -77,30 +81,32 @@ SDwindleNodeData* CHyprDwindleLayout::getClosestNodeOnWorkspace(const WORKSPACEI return res; } -SDwindleNodeData* CHyprDwindleLayout::getNodeFromWindow(PHLWINDOW pWindow) { +SP CHyprDwindleLayout::getNodeFromWindow(PHLWINDOW pWindow) { for (auto& n : m_dwindleNodesData) { - if (n.pWindow.lock() == pWindow && !n.isNode) - return &n; + if (n->pWindow.lock() == pWindow && !n->isNode) + return n; } return nullptr; } -SDwindleNodeData* CHyprDwindleLayout::getMasterNodeOnWorkspace(const WORKSPACEID& id) { +SP CHyprDwindleLayout::getMasterNodeOnWorkspace(const WORKSPACEID& id) { for (auto& n : m_dwindleNodesData) { - if (!n.pParent && n.workspaceID == id) - return &n; + if (!n->pParent && n->workspaceID == id) + return n; } return nullptr; } -void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool force) { +void CHyprDwindleLayout::applyNodeDataToWindow(SP pNode, bool force) { // Don't set nodes, only windows. if (pNode->isNode) return; PHLMONITOR PMONITOR = nullptr; + const auto WS = g_pCompositor->getWorkspaceByID(pNode->workspaceID); + if (g_pCompositor->isWorkspaceSpecial(pNode->workspaceID)) { for (auto const& m : g_pCompositor->m_monitors) { if (m->activeSpecialWorkspaceID() == pNode->workspaceID) { @@ -108,19 +114,20 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for break; } } - } else if (const auto WS = g_pCompositor->getWorkspaceByID(pNode->workspaceID); WS) + } else if (WS) PMONITOR = WS->m_monitor.lock(); - if (!PMONITOR) { + if (!PMONITOR || !WS) { Debug::log(ERR, "Orphaned Node {}!!", pNode); return; } // for gaps outer - const bool DISPLAYLEFT = STICKS(pNode->box.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x); - const bool DISPLAYRIGHT = STICKS(pNode->box.x + pNode->box.w, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x); - const bool DISPLAYTOP = STICKS(pNode->box.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y); - const bool DISPLAYBOTTOM = STICKS(pNode->box.y + pNode->box.h, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y); + const auto MONITOR_WORKAREA = workAreaOnWorkspace(WS); + const bool DISPLAYLEFT = STICKS(pNode->box.x, MONITOR_WORKAREA.x); + const bool DISPLAYRIGHT = STICKS(pNode->box.x + pNode->box.w, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w); + const bool DISPLAYTOP = STICKS(pNode->box.y, MONITOR_WORKAREA.y); + const bool DISPLAYBOTTOM = STICKS(pNode->box.y + pNode->box.h, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h); const auto PWINDOW = pNode->pWindow.lock(); // get specific gaps and rules for this workspace, @@ -139,13 +146,10 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for PWINDOW->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT); PWINDOW->updateWindowData(); - static auto PGAPSINDATA = CConfigValue("general:gaps_in"); - static auto PGAPSOUTDATA = CConfigValue("general:gaps_out"); - auto* const PGAPSIN = sc((PGAPSINDATA.ptr())->getData()); - auto* const PGAPSOUT = sc((PGAPSOUTDATA.ptr())->getData()); + static auto PGAPSINDATA = CConfigValue("general:gaps_in"); + auto* const PGAPSIN = sc((PGAPSINDATA.ptr())->getData()); auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN); - auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT); CBox nodeBox = pNode->box; nodeBox.round(); @@ -163,7 +167,7 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for Vector2D ratioPadding; if ((*REQUESTEDRATIO).y != 0 && !pNode->pParent) { - const Vector2D originalSize = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight; + const Vector2D originalSize = MONITOR_WORKAREA.size(); const double requestedRatio = (*REQUESTEDRATIO).x / (*REQUESTEDRATIO).y; const double originalRatio = originalSize.x / originalSize.y; @@ -181,9 +185,9 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for } } - const auto GAPOFFSETTOPLEFT = Vector2D(sc(DISPLAYLEFT ? gapsOut.m_left : gapsIn.m_left), sc(DISPLAYTOP ? gapsOut.m_top : gapsIn.m_top)); + const auto GAPOFFSETTOPLEFT = Vector2D(sc(DISPLAYLEFT ? 0 : gapsIn.m_left), sc(DISPLAYTOP ? 0 : gapsIn.m_top)); - const auto GAPOFFSETBOTTOMRIGHT = Vector2D(sc(DISPLAYRIGHT ? gapsOut.m_right : gapsIn.m_right), sc(DISPLAYBOTTOM ? gapsOut.m_bottom : gapsIn.m_bottom)); + const auto GAPOFFSETBOTTOMRIGHT = Vector2D(sc(DISPLAYRIGHT ? 0 : gapsIn.m_right), sc(DISPLAYBOTTOM ? 0 : gapsIn.m_bottom)); calcPos = calcPos + GAPOFFSETTOPLEFT + ratioPadding / 2; calcSize = calcSize - GAPOFFSETTOPLEFT - GAPOFFSETBOTTOMRIGHT - ratioPadding; @@ -222,20 +226,17 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for if (*PCLAMP_TILED) { const auto borderSize = PWINDOW->getRealBorderSize(); - Vector2D monitorAvailable = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight - - Vector2D{(double)(gapsOut.m_left + gapsOut.m_right), (double)(gapsOut.m_top + gapsOut.m_bottom)} - Vector2D{2.0 * borderSize, 2.0 * borderSize}; + Vector2D monitorAvailable = MONITOR_WORKAREA.size() - Vector2D{2.0 * borderSize, 2.0 * borderSize}; - Vector2D minSize = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable); - Vector2D maxSize = PWINDOW->isFullscreen() ? Vector2D{INFINITY, INFINITY} : - PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable); - calcSize = calcSize.clamp(minSize, maxSize); + Vector2D minSize = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable); + Vector2D maxSize = PWINDOW->isFullscreen() ? Vector2D{INFINITY, INFINITY} : + PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable); + calcSize = calcSize.clamp(minSize, maxSize); calcPos += (availableSpace - calcSize) / 2.0; - calcPos.x = std::clamp(calcPos.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x + gapsOut.m_left + borderSize, - PMONITOR->m_size.x + PMONITOR->m_position.x - PMONITOR->m_reservedBottomRight.x - gapsOut.m_right - calcSize.x - borderSize); - calcPos.y = std::clamp(calcPos.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y + gapsOut.m_top + borderSize, - PMONITOR->m_size.y + PMONITOR->m_position.y - PMONITOR->m_reservedBottomRight.y - gapsOut.m_bottom - calcSize.y - borderSize); + 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 + borderSize, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h - calcSize.y - borderSize); } if (PWINDOW->onSpecialWorkspace() && !PWINDOW->isFullscreen()) { @@ -271,8 +272,8 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir if (pWindow->m_isFloating) return; - m_dwindleNodesData.emplace_back(); - const auto PNODE = &m_dwindleNodesData.back(); + const auto PNODE = m_dwindleNodesData.emplace_back(makeShared()); + PNODE->self = PNODE; const auto PMONITOR = pWindow->m_monitor.lock(); @@ -288,10 +289,10 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir PNODE->isNode = false; PNODE->layout = this; - SDwindleNodeData* OPENINGON; + SP OPENINGON; - const auto MOUSECOORDS = m_overrideFocalPoint.value_or(g_pInputManager->getMouseCoordsInternal()); - const auto MONFROMCURSOR = g_pCompositor->getMonitorFromVector(MOUSECOORDS); + const auto MOUSECOORDS = m_overrideFocalPoint.value_or(g_pInputManager->getMouseCoordsInternal()); + const auto MONFROMCURSOR = g_pCompositor->getMonitorFromVector(MOUSECOORDS); if (PMONITOR->m_id == MONFROMCURSOR->m_id && (PNODE->workspaceID == PMONITOR->activeWorkspaceID() || (g_pCompositor->isWorkspaceSpecial(PNODE->workspaceID) && PMONITOR->m_activeSpecialWorkspace)) && !*PUSEACTIVE) { @@ -326,7 +327,7 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir if (const auto MAXSIZE = pWindow->requestedMaxSize(); MAXSIZE.x < PREDSIZEMAX.x || MAXSIZE.y < PREDSIZEMAX.y) { // we can't continue. make it floating. pWindow->m_isFloating = true; - m_dwindleNodesData.remove(*PNODE); + std::erase(m_dwindleNodesData, PNODE); g_pLayoutManager->getCurrentLayout()->onWindowCreatedFloating(pWindow); return; } @@ -334,8 +335,8 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir // last fail-safe to avoid duplicate fullscreens if ((!OPENINGON || OPENINGON->pWindow.lock() == pWindow) && getNodesOnWorkspace(PNODE->workspaceID) > 1) { for (auto& node : m_dwindleNodesData) { - if (node.workspaceID == PNODE->workspaceID && node.pWindow.lock() && node.pWindow.lock() != pWindow) { - OPENINGON = &node; + if (node->workspaceID == PNODE->workspaceID && node->pWindow.lock() && node->pWindow.lock() != pWindow) { + OPENINGON = node; break; } } @@ -343,17 +344,14 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir // if it's the first, it's easy. Make it fullscreen. if (!OPENINGON || OPENINGON->pWindow.lock() == pWindow) { - PNODE->box = CBox{PMONITOR->m_position + PMONITOR->m_reservedTopLeft, PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight}; - + PNODE->applyRootBox(); applyNodeDataToWindow(PNODE); - return; } // get the node under our cursor - m_dwindleNodesData.emplace_back(); - const auto NEWPARENT = &m_dwindleNodesData.back(); + const auto NEWPARENT = m_dwindleNodesData.emplace_back(makeShared()); // make the parent have the OPENINGON's stats NEWPARENT->box = OPENINGON->box; @@ -361,6 +359,7 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir NEWPARENT->pParent = OPENINGON->pParent; NEWPARENT->isNode = true; // it is a node NEWPARENT->splitRatio = std::clamp(*PDEFAULTSPLIT, 0.1f, 1.9f); + NEWPARENT->layout = this; static auto PWIDTHMULTIPLIER = CConfigValue("dwindle:split_width_multiplier"); @@ -503,7 +502,7 @@ void CHyprDwindleLayout::onWindowRemovedTiling(PHLWINDOW pWindow) { if (!PPARENT) { Debug::log(LOG, "Removing last node (dwindle)"); - m_dwindleNodesData.remove(*PNODE); + std::erase(m_dwindleNodesData, PNODE); return; } @@ -528,8 +527,8 @@ void CHyprDwindleLayout::onWindowRemovedTiling(PHLWINDOW pWindow) { else PSIBLING->recalcSizePosRecursive(); - m_dwindleNodesData.remove(*PPARENT); - m_dwindleNodesData.remove(*PNODE); + std::erase(m_dwindleNodesData, PPARENT); + std::erase(m_dwindleNodesData, PNODE); pWindow->m_workspace->updateWindows(); } @@ -568,15 +567,17 @@ void CHyprDwindleLayout::calculateWorkspace(const PHLWORKSPACE& pWorkspace) { *PFULLWINDOW->m_realPosition = PMONITOR->m_position; *PFULLWINDOW->m_realSize = PMONITOR->m_size; } else if (pWorkspace->m_fullscreenMode == FSMODE_MAXIMIZED) { - SDwindleNodeData fakeNode; - fakeNode.pWindow = PFULLWINDOW; - fakeNode.box = {PMONITOR->m_position + PMONITOR->m_reservedTopLeft, PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight}; - fakeNode.workspaceID = pWorkspace->m_id; - PFULLWINDOW->m_position = fakeNode.box.pos(); - PFULLWINDOW->m_size = fakeNode.box.size(); - fakeNode.ignoreFullscreenChecks = true; - - applyNodeDataToWindow(&fakeNode); + SP fakeNode = makeShared(); + fakeNode->self = fakeNode; + fakeNode->pWindow = PFULLWINDOW; + fakeNode->box = PMONITOR->logicalBoxMinusReserved(); + fakeNode->workspaceID = pWorkspace->m_id; + PFULLWINDOW->m_position = fakeNode->box.pos(); + PFULLWINDOW->m_size = fakeNode->box.size(); + fakeNode->ignoreFullscreenChecks = true; + fakeNode->layout = this; + + applyNodeDataToWindow(fakeNode); } // if has fullscreen, don't calculate the rest @@ -586,7 +587,7 @@ void CHyprDwindleLayout::calculateWorkspace(const PHLWORKSPACE& pWorkspace) { const auto TOPNODE = getMasterNodeOnWorkspace(pWorkspace->m_id); if (TOPNODE) { - TOPNODE->box = {PMONITOR->m_position + PMONITOR->m_reservedTopLeft, PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight}; + TOPNODE->applyRootBox(); TOPNODE->recalcSizePosRecursive(); } } @@ -622,11 +623,12 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn static auto PSMARTRESIZING = CConfigValue("dwindle:smart_resizing"); // get some data about our window - const auto PMONITOR = PWINDOW->m_monitor.lock(); - const bool DISPLAYLEFT = STICKS(PWINDOW->m_position.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x); - const bool DISPLAYRIGHT = STICKS(PWINDOW->m_position.x + PWINDOW->m_size.x, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x); - const bool DISPLAYTOP = STICKS(PWINDOW->m_position.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y); - const bool DISPLAYBOTTOM = STICKS(PWINDOW->m_position.y + PWINDOW->m_size.y, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y); + const auto PMONITOR = PWINDOW->m_monitor.lock(); + const auto MONITOR_WORKAREA = PMONITOR->logicalBoxMinusReserved(); + const bool DISPLAYLEFT = STICKS(PWINDOW->m_position.x, MONITOR_WORKAREA.x); + const bool DISPLAYRIGHT = STICKS(PWINDOW->m_position.x + PWINDOW->m_size.x, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w); + const bool DISPLAYTOP = STICKS(PWINDOW->m_position.y, MONITOR_WORKAREA.y); + const bool DISPLAYBOTTOM = STICKS(PWINDOW->m_position.y + PWINDOW->m_size.y, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h); if (PWINDOW->m_isPseudotiled) { if (!m_pseudoDragFlags.started) { @@ -682,18 +684,18 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn if (*PSMARTRESIZING == 1) { // Identify inner and outer nodes for both directions - SDwindleNodeData* PVOUTER = nullptr; - SDwindleNodeData* PVINNER = nullptr; - SDwindleNodeData* PHOUTER = nullptr; - SDwindleNodeData* PHINNER = nullptr; - - const auto LEFT = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT || DISPLAYRIGHT; - const auto TOP = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT || DISPLAYBOTTOM; - const auto RIGHT = corner == CORNER_TOPRIGHT || corner == CORNER_BOTTOMRIGHT || DISPLAYLEFT; - const auto BOTTOM = corner == CORNER_BOTTOMLEFT || corner == CORNER_BOTTOMRIGHT || DISPLAYTOP; - const auto NONE = corner == CORNER_NONE; - - for (auto PCURRENT = PNODE; PCURRENT && PCURRENT->pParent; PCURRENT = PCURRENT->pParent) { + SP PVOUTER = nullptr; + SP PVINNER = nullptr; + SP PHOUTER = nullptr; + SP PHINNER = nullptr; + + const auto LEFT = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT || DISPLAYRIGHT; + const auto TOP = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT || DISPLAYBOTTOM; + const auto RIGHT = corner == CORNER_TOPRIGHT || corner == CORNER_BOTTOMRIGHT || DISPLAYLEFT; + const auto BOTTOM = corner == CORNER_BOTTOMLEFT || corner == CORNER_BOTTOMRIGHT || DISPLAYTOP; + const auto NONE = corner == CORNER_NONE; + + for (auto PCURRENT = PNODE; PCURRENT && PCURRENT->pParent; PCURRENT = PCURRENT->pParent.lock()) { const auto PPARENT = PCURRENT->pParent; if (!PVOUTER && PPARENT->splitTop && (NONE || (TOP && PPARENT->children[1] == PCURRENT) || (BOTTOM && PPARENT->children[0] == PCURRENT))) @@ -833,15 +835,17 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFu // We make a fake "only" node and apply // To keep consistent with the settings without C+P code - SDwindleNodeData fakeNode; - fakeNode.pWindow = pWindow; - fakeNode.box = {PMONITOR->m_position + PMONITOR->m_reservedTopLeft, PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight}; - fakeNode.workspaceID = pWindow->workspaceID(); - pWindow->m_position = fakeNode.box.pos(); - pWindow->m_size = fakeNode.box.size(); - fakeNode.ignoreFullscreenChecks = true; - - applyNodeDataToWindow(&fakeNode); + SP fakeNode = makeShared(); + fakeNode->self = fakeNode; + fakeNode->pWindow = pWindow; + fakeNode->box = PMONITOR->logicalBoxMinusReserved(); + fakeNode->workspaceID = pWindow->workspaceID(); + pWindow->m_position = fakeNode->box.pos(); + pWindow->m_size = fakeNode->box.size(); + fakeNode->ignoreFullscreenChecks = true; + fakeNode->layout = this; + + applyNodeDataToWindow(fakeNode); } } @@ -1092,10 +1096,10 @@ void CHyprDwindleLayout::moveToRoot(PHLWINDOW pWindow, bool stable) { // instead of [getMasterNodeOnWorkspace], we walk back to root since we need // to know which children of root is our ancestor - auto pAncestor = PNODE, pRoot = PNODE->pParent; + auto pAncestor = PNODE, pRoot = PNODE->pParent.lock(); while (pRoot->pParent) { pAncestor = pRoot; - pRoot = pRoot->pParent; + pRoot = pRoot->pParent.lock(); } auto& pSwap = pRoot->children[0] == pAncestor ? pRoot->children[1] : pRoot->children[0]; diff --git a/src/layout/DwindleLayout.hpp b/src/layout/DwindleLayout.hpp index 23f19956a9d..e704b8f4aba 100644 --- a/src/layout/DwindleLayout.hpp +++ b/src/layout/DwindleLayout.hpp @@ -13,24 +13,25 @@ class CHyprDwindleLayout; enum eFullscreenMode : int8_t; struct SDwindleNodeData { - SDwindleNodeData* pParent = nullptr; - bool isNode = false; + WP pParent; + bool isNode = false; - PHLWINDOWREF pWindow; + PHLWINDOWREF pWindow; - std::array children = {nullptr, nullptr}; + std::array, 2> children = {}; + WP self; - bool splitTop = false; // for preserve_split + bool splitTop = false; // for preserve_split - CBox box = {0}; + CBox box = {0}; - WORKSPACEID workspaceID = WORKSPACE_INVALID; + WORKSPACEID workspaceID = WORKSPACE_INVALID; - float splitRatio = 1.f; + float splitRatio = 1.f; - bool valid = true; + bool valid = true; - bool ignoreFullscreenChecks = false; + bool ignoreFullscreenChecks = false; // For list lookup bool operator==(const SDwindleNodeData& rhs) const { @@ -39,6 +40,7 @@ struct SDwindleNodeData { } void recalcSizePosRecursive(bool force = false, bool horizontalOverride = false, bool verticalOverride = false); + void applyRootBox(); CHyprDwindleLayout* layout = nullptr; }; @@ -65,7 +67,7 @@ class CHyprDwindleLayout : public IHyprLayout { virtual void onDisable(); private: - std::list m_dwindleNodesData; + std::vector> m_dwindleNodesData; struct { bool started = false; @@ -77,12 +79,12 @@ class CHyprDwindleLayout : public IHyprLayout { std::optional m_overrideFocalPoint; // for onWindowCreatedTiling. int getNodesOnWorkspace(const WORKSPACEID&); - void applyNodeDataToWindow(SDwindleNodeData*, bool force = false); + void applyNodeDataToWindow(SP, bool force = false); void calculateWorkspace(const PHLWORKSPACE& pWorkspace); - SDwindleNodeData* getNodeFromWindow(PHLWINDOW); - SDwindleNodeData* getFirstNodeOnWorkspace(const WORKSPACEID&); - SDwindleNodeData* getClosestNodeOnWorkspace(const WORKSPACEID&, const Vector2D&); - SDwindleNodeData* getMasterNodeOnWorkspace(const WORKSPACEID&); + SP getNodeFromWindow(PHLWINDOW); + SP getFirstNodeOnWorkspace(const WORKSPACEID&); + SP getClosestNodeOnWorkspace(const WORKSPACEID&, const Vector2D&); + SP getMasterNodeOnWorkspace(const WORKSPACEID&); void toggleSplit(PHLWINDOW); void swapSplit(PHLWINDOW); @@ -94,13 +96,13 @@ class CHyprDwindleLayout : public IHyprLayout { }; template -struct std::formatter : std::formatter { +struct std::formatter, CharT> : std::formatter { template - auto format(const SDwindleNodeData* const& node, FormatContext& ctx) const { + auto format(const SP& node, FormatContext& ctx) const { auto out = ctx.out(); if (!node) return std::format_to(out, "[Node nullptr]"); - std::format_to(out, "[Node {:x}: workspace: {}, pos: {:j2}, size: {:j2}", rc(node), node->workspaceID, node->box.pos(), node->box.size()); + std::format_to(out, "[Node {:x}: workspace: {}, pos: {:j2}, size: {:j2}", rc(node.get()), node->workspaceID, node->box.pos(), node->box.size()); if (!node->isNode && !node->pWindow.expired()) std::format_to(out, ", window: {:x}", node->pWindow.lock()); return std::format_to(out, "]"); diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp index fb47938fc4d..e86f00bcd79 100644 --- a/src/layout/IHyprLayout.cpp +++ b/src/layout/IHyprLayout.cpp @@ -503,32 +503,35 @@ void IHyprLayout::performSnap(Vector2D& sourcePos, Vector2D& sourceSize, PHLWIND const auto EXTENTNONE = SBoxExtents{{0, 0}, {0, 0}}; const auto* EXTENTDIFF = *SNAPBORDEROVERLAP ? &EXTENTS : &EXTENTNONE; const auto MON = DRAGGINGWINDOW->m_monitor.lock(); - const auto* GAPSOUT = *SNAPRESPECTGAPS ? sc(PGAPSOUT.ptr()->getData()) : &GAPSNONE; - SRange monX = {MON->m_position.x + MON->m_reservedTopLeft.x + GAPSOUT->m_left, MON->m_position.x + MON->m_size.x - MON->m_reservedBottomRight.x - GAPSOUT->m_right}; - SRange monY = {MON->m_position.y + MON->m_reservedTopLeft.y + GAPSOUT->m_top, MON->m_position.y + MON->m_size.y - MON->m_reservedBottomRight.y - GAPSOUT->m_bottom}; + const auto* GAPSOUT = *SNAPRESPECTGAPS ? sc(PGAPSOUT.ptr()->getData()) : &GAPSNONE; + const auto WORK_AREA = Desktop::CReservedArea{GAPSOUT->m_top, GAPSOUT->m_right, GAPSOUT->m_bottom, GAPSOUT->m_left}.apply(MON->logicalBoxMinusReserved()); + + SRange monX = {WORK_AREA.x, WORK_AREA.x + WORK_AREA.w}; + SRange monY = {WORK_AREA.y, WORK_AREA.y + WORK_AREA.h}; + + const bool HAS_LEFT = MON->m_reservedArea.left() > 0; + const bool HAS_TOP = MON->m_reservedArea.top() > 0; + const bool HAS_BOTTOM = MON->m_reservedArea.bottom() > 0; + const bool HAS_RIGHT = MON->m_reservedArea.right() > 0; if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) && - ((MON->m_reservedTopLeft.x > 0 && canSnap(sourceX.start, monX.start, GAPSIZE)) || - canSnap(sourceX.start, (monX.start -= MON->m_reservedTopLeft.x + EXTENTDIFF->topLeft.x), GAPSIZE))) { + ((HAS_LEFT && canSnap(sourceX.start, monX.start, GAPSIZE)) || canSnap(sourceX.start, (monX.start -= MON->m_reservedArea.left() + EXTENTDIFF->topLeft.x), GAPSIZE))) { SNAP(sourceX.start, sourceX.end, monX.start); snaps |= SNAP_LEFT; } if (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) && - ((MON->m_reservedBottomRight.x > 0 && canSnap(sourceX.end, monX.end, GAPSIZE)) || - canSnap(sourceX.end, (monX.end += MON->m_reservedBottomRight.x + EXTENTDIFF->bottomRight.x), GAPSIZE))) { + ((HAS_RIGHT && canSnap(sourceX.end, monX.end, GAPSIZE)) || canSnap(sourceX.end, (monX.end += MON->m_reservedArea.right() + EXTENTDIFF->bottomRight.x), GAPSIZE))) { SNAP(sourceX.end, sourceX.start, monX.end); snaps |= SNAP_RIGHT; } if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) && - ((MON->m_reservedTopLeft.y > 0 && canSnap(sourceY.start, monY.start, GAPSIZE)) || - canSnap(sourceY.start, (monY.start -= MON->m_reservedTopLeft.y + EXTENTDIFF->topLeft.y), GAPSIZE))) { + ((HAS_TOP && canSnap(sourceY.start, monY.start, GAPSIZE)) || canSnap(sourceY.start, (monY.start -= MON->m_reservedArea.top() + EXTENTDIFF->topLeft.y), GAPSIZE))) { SNAP(sourceY.start, sourceY.end, monY.start); snaps |= SNAP_UP; } if (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) && - ((MON->m_reservedBottomRight.y > 0 && canSnap(sourceY.end, monY.end, GAPSIZE)) || - canSnap(sourceY.end, (monY.end += MON->m_reservedBottomRight.y + EXTENTDIFF->bottomRight.y), GAPSIZE))) { + ((HAS_BOTTOM && canSnap(sourceY.end, monY.end, GAPSIZE)) || canSnap(sourceY.end, (monY.end += MON->m_reservedArea.bottom() + EXTENTDIFF->bottomRight.y), GAPSIZE))) { SNAP(sourceY.end, sourceY.start, monY.end); snaps |= SNAP_DOWN; } @@ -832,7 +835,7 @@ void IHyprLayout::fitFloatingWindowOnMonitor(PHLWINDOW w, std::optional tb const auto EXTENTS = w->getWindowExtentsUnified(RESERVED_EXTENTS | INPUT_EXTENTS); CBox targetBoxMonLocal = tb.value_or(w->getWindowMainSurfaceBox()).translate(-PMONITOR->m_position).addExtents(EXTENTS); - const auto MONITOR_LOCAL_BOX = PMONITOR->logicalBoxMinusExtents().translate(-PMONITOR->m_position); + const auto MONITOR_LOCAL_BOX = PMONITOR->logicalBoxMinusReserved().translate(-PMONITOR->m_position); if (targetBoxMonLocal.w < MONITOR_LOCAL_BOX.w) { if (targetBoxMonLocal.x < MONITOR_LOCAL_BOX.x) @@ -1039,3 +1042,22 @@ bool IHyprLayout::updateDragWindow() { return false; } + +CBox IHyprLayout::workAreaOnWorkspace(const PHLWORKSPACE& pWorkspace) { + if (!pWorkspace || !pWorkspace->m_monitor) + return {}; + + const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(pWorkspace); + + auto workArea = pWorkspace->m_monitor->logicalBoxMinusReserved(); + + static auto PGAPSOUTDATA = CConfigValue("general:gaps_out"); + auto* const PGAPSOUT = sc((PGAPSOUTDATA.ptr())->getData()); + auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT); + + Desktop::CReservedArea reservedGaps{gapsOut.m_top, gapsOut.m_right, gapsOut.m_bottom, gapsOut.m_left}; + + reservedGaps.applyip(workArea); + + return workArea; +} diff --git a/src/layout/IHyprLayout.hpp b/src/layout/IHyprLayout.hpp index d97a2ba8e9c..ad19700d00e 100644 --- a/src/layout/IHyprLayout.hpp +++ b/src/layout/IHyprLayout.hpp @@ -230,6 +230,12 @@ class IHyprLayout { */ virtual void fitFloatingWindowOnMonitor(PHLWINDOW w, std::optional targetBox = std::nullopt); + /* + Returns a logical box describing the work area on a workspace + (monitor size - reserved - gapsOut) + */ + virtual CBox workAreaOnWorkspace(const PHLWORKSPACE& pWorkspace); + private: int m_mouseMoveEventCount; Vector2D m_beginDragXY; diff --git a/src/layout/MasterLayout.cpp b/src/layout/MasterLayout.cpp index d0b82343a06..a998d3c8e7f 100644 --- a/src/layout/MasterLayout.cpp +++ b/src/layout/MasterLayout.cpp @@ -322,8 +322,9 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { } else if (pWorkspace->m_fullscreenMode == FSMODE_MAXIMIZED) { SMasterNodeData fakeNode; fakeNode.pWindow = PFULLWINDOW; - fakeNode.position = PMONITOR->m_position + PMONITOR->m_reservedTopLeft; - fakeNode.size = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight; + const auto WORKAREA = PMONITOR->logicalBoxMinusReserved(); + fakeNode.position = WORKAREA.pos(); + fakeNode.size = WORKAREA.size(); fakeNode.workspaceID = pWorkspace->m_id; PFULLWINDOW->m_position = fakeNode.position; PFULLWINDOW->m_size = fakeNode.size; @@ -351,13 +352,12 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { const auto MASTERS = getMastersOnWorkspace(pWorkspace->m_id); const auto WINDOWS = getNodesOnWorkspace(pWorkspace->m_id); const auto STACKWINDOWS = WINDOWS - MASTERS; - const auto WSSIZE = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight; - const auto WSPOS = PMONITOR->m_position + PMONITOR->m_reservedTopLeft; + const auto WORKAREA = workAreaOnWorkspace(pWorkspace); if (orientation == ORIENTATION_CENTER) { - if (STACKWINDOWS >= *SLAVECOUNTFORCENTER) { + if (STACKWINDOWS >= *SLAVECOUNTFORCENTER) centerMasterWindow = true; - } else { + else { if (*CMFALLBACK == "left") orientation = ORIENTATION_LEFT; else if (*CMFALLBACK == "right") @@ -371,7 +371,7 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { } } - const float totalSize = (orientation == ORIENTATION_TOP || orientation == ORIENTATION_BOTTOM) ? WSSIZE.x : WSSIZE.y; + const float totalSize = (orientation == ORIENTATION_TOP || orientation == ORIENTATION_BOTTOM) ? WORKAREA.w : WORKAREA.h; const float masterAverageSize = totalSize / MASTERS; const float slaveAverageSize = totalSize / STACKWINDOWS; float masterAccumulatedSize = 0; @@ -394,32 +394,32 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { if (WINDOWS == 1 && !centerMasterWindow) { static auto PALWAYSKEEPPOSITION = CConfigValue("master:always_keep_position"); if (*PALWAYSKEEPPOSITION) { - const float WIDTH = WSSIZE.x * PMASTERNODE->percMaster; + const float WIDTH = WORKAREA.w * PMASTERNODE->percMaster; float nextX = 0; if (orientation == ORIENTATION_RIGHT) - nextX = WSSIZE.x - WIDTH; + nextX = WORKAREA.w - WIDTH; else if (orientation == ORIENTATION_CENTER) - nextX = (WSSIZE.x - WIDTH) / 2; + nextX = (WORKAREA.w - WIDTH) / 2; - PMASTERNODE->size = Vector2D(WIDTH, WSSIZE.y); - PMASTERNODE->position = WSPOS + Vector2D(nextX, 0.0); + PMASTERNODE->size = Vector2D(WIDTH, WORKAREA.h); + PMASTERNODE->position = WORKAREA.pos() + Vector2D(nextX, 0.0); } else { - PMASTERNODE->size = WSSIZE; - PMASTERNODE->position = WSPOS; + PMASTERNODE->size = WORKAREA.size(); + PMASTERNODE->position = WORKAREA.pos(); } applyNodeDataToWindow(PMASTERNODE); return; } else if (orientation == ORIENTATION_TOP || orientation == ORIENTATION_BOTTOM) { - const float HEIGHT = STACKWINDOWS != 0 ? WSSIZE.y * PMASTERNODE->percMaster : WSSIZE.y; - float widthLeft = WSSIZE.x; + const float HEIGHT = STACKWINDOWS != 0 ? WORKAREA.h * PMASTERNODE->percMaster : WORKAREA.h; + float widthLeft = WORKAREA.w; int mastersLeft = MASTERS; float nextX = 0; float nextY = 0; if (orientation == ORIENTATION_BOTTOM) - nextY = WSSIZE.y - HEIGHT; + nextY = WORKAREA.h - HEIGHT; for (auto& nd : m_masterNodesData) { if (nd.workspaceID != pWorkspace->m_id || !nd.isMaster) @@ -430,12 +430,12 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { WIDTH = widthLeft * 0.9f; if (*PSMARTRESIZING) { - nd.percSize *= WSSIZE.x / masterAccumulatedSize; + nd.percSize *= WORKAREA.w / masterAccumulatedSize; WIDTH = masterAverageSize * nd.percSize; } nd.size = Vector2D(WIDTH, HEIGHT); - nd.position = WSPOS + Vector2D(nextX, nextY); + nd.position = WORKAREA.pos() + Vector2D(nextX, nextY); applyNodeDataToWindow(&nd); mastersLeft--; @@ -443,8 +443,8 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { nextX += WIDTH; } } else { // orientation left, right or center - float WIDTH = *PIGNORERESERVED && centerMasterWindow ? PMONITOR->m_size.x : WSSIZE.x; - float heightLeft = WSSIZE.y; + float WIDTH = *PIGNORERESERVED && centerMasterWindow ? PMONITOR->m_size.x : WORKAREA.w; + float heightLeft = WORKAREA.h; int mastersLeft = MASTERS; float nextX = 0; float nextY = 0; @@ -452,11 +452,10 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { if (STACKWINDOWS > 0 || centerMasterWindow) WIDTH *= PMASTERNODE->percMaster; - if (orientation == ORIENTATION_RIGHT) { - nextX = WSSIZE.x - WIDTH; - } else if (centerMasterWindow) { - nextX = ((*PIGNORERESERVED && centerMasterWindow ? PMONITOR->m_size.x : WSSIZE.x) - WIDTH) / 2; - } + if (orientation == ORIENTATION_RIGHT) + nextX = WORKAREA.w - WIDTH; + else if (centerMasterWindow) + nextX = ((*PIGNORERESERVED && centerMasterWindow ? PMONITOR->m_size.x : WORKAREA.w) - WIDTH) / 2; for (auto& nd : m_masterNodesData) { if (nd.workspaceID != pWorkspace->m_id || !nd.isMaster) @@ -467,12 +466,12 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { HEIGHT = heightLeft * 0.9f; if (*PSMARTRESIZING) { - nd.percSize *= WSSIZE.y / masterAccumulatedSize; + nd.percSize *= WORKAREA.h / masterAccumulatedSize; HEIGHT = masterAverageSize * nd.percSize; } nd.size = Vector2D(WIDTH, HEIGHT); - nd.position = (*PIGNORERESERVED && centerMasterWindow ? PMONITOR->m_position : WSPOS) + Vector2D(nextX, nextY); + nd.position = (*PIGNORERESERVED && centerMasterWindow ? PMONITOR->m_position : WORKAREA.pos()) + Vector2D(nextX, nextY); applyNodeDataToWindow(&nd); mastersLeft--; @@ -487,8 +486,8 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { // compute placement of slave window(s) int slavesLeft = STACKWINDOWS; if (orientation == ORIENTATION_TOP || orientation == ORIENTATION_BOTTOM) { - const float HEIGHT = WSSIZE.y - PMASTERNODE->size.y; - float widthLeft = WSSIZE.x; + const float HEIGHT = WORKAREA.h - PMASTERNODE->size.y; + float widthLeft = WORKAREA.w; float nextX = 0; float nextY = 0; @@ -504,12 +503,12 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { WIDTH = widthLeft * 0.9f; if (*PSMARTRESIZING) { - nd.percSize *= WSSIZE.x / slaveAccumulatedSize; + nd.percSize *= WORKAREA.w / slaveAccumulatedSize; WIDTH = slaveAverageSize * nd.percSize; } nd.size = Vector2D(WIDTH, HEIGHT); - nd.position = WSPOS + Vector2D(nextX, nextY); + nd.position = WORKAREA.pos() + Vector2D(nextX, nextY); applyNodeDataToWindow(&nd); slavesLeft--; @@ -517,8 +516,8 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { nextX += WIDTH; } } else if (orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT) { - const float WIDTH = WSSIZE.x - PMASTERNODE->size.x; - float heightLeft = WSSIZE.y; + const float WIDTH = WORKAREA.w - PMASTERNODE->size.x; + float heightLeft = WORKAREA.h; float nextY = 0; float nextX = 0; @@ -534,12 +533,12 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { HEIGHT = heightLeft * 0.9f; if (*PSMARTRESIZING) { - nd.percSize *= WSSIZE.y / slaveAccumulatedSize; + nd.percSize *= WORKAREA.h / slaveAccumulatedSize; HEIGHT = slaveAverageSize * nd.percSize; } nd.size = Vector2D(WIDTH, HEIGHT); - nd.position = WSPOS + Vector2D(nextX, nextY); + nd.position = WORKAREA.pos() + Vector2D(nextX, nextY); applyNodeDataToWindow(&nd); slavesLeft--; @@ -547,10 +546,10 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { nextY += HEIGHT; } } else { // slaves for centered master window(s) - const float WIDTH = ((*PIGNORERESERVED ? PMONITOR->m_size.x : WSSIZE.x) - PMASTERNODE->size.x) / 2.0; + const float WIDTH = ((*PIGNORERESERVED ? PMONITOR->m_size.x : WORKAREA.w) - PMASTERNODE->size.x) / 2.0; float heightLeft = 0; - float heightLeftL = WSSIZE.y; - float heightLeftR = WSSIZE.y; + float heightLeftL = WORKAREA.h; + float heightLeftR = WORKAREA.h; float nextX = 0; float nextY = 0; float nextYL = 0; @@ -564,8 +563,8 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { slavesLeftL = slavesLeft - slavesLeftR; } - const float slaveAverageHeightL = WSSIZE.y / slavesLeftL; - const float slaveAverageHeightR = WSSIZE.y / slavesLeftR; + const float slaveAverageHeightL = WORKAREA.h / slavesLeftL; + const float slaveAverageHeightR = WORKAREA.h / slavesLeftR; float slaveAccumulatedHeightL = 0; float slaveAccumulatedHeightR = 0; @@ -590,7 +589,7 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { continue; if (onRight) { - nextX = WIDTH + PMASTERNODE->size.x - (*PIGNORERESERVED ? PMONITOR->m_reservedTopLeft.x : 0); + nextX = WIDTH + PMASTERNODE->size.x - (*PIGNORERESERVED ? PMONITOR->m_reservedArea.left() : 0); nextY = nextYR; heightLeft = heightLeftR; slavesLeft = slavesLeftR; @@ -607,16 +606,16 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { if (*PSMARTRESIZING) { if (onRight) { - nd.percSize *= WSSIZE.y / slaveAccumulatedHeightR; + nd.percSize *= WORKAREA.h / slaveAccumulatedHeightR; HEIGHT = slaveAverageHeightR * nd.percSize; } else { - nd.percSize *= WSSIZE.y / slaveAccumulatedHeightL; + nd.percSize *= WORKAREA.h / slaveAccumulatedHeightL; HEIGHT = slaveAverageHeightL * nd.percSize; } } - nd.size = Vector2D(*PIGNORERESERVED ? (WIDTH - (onRight ? PMONITOR->m_reservedBottomRight.x : PMONITOR->m_reservedTopLeft.x)) : WIDTH, HEIGHT); - nd.position = WSPOS + Vector2D(nextX, nextY); + nd.size = Vector2D(*PIGNORERESERVED ? (WIDTH - (onRight ? PMONITOR->m_reservedArea.right() : PMONITOR->m_reservedArea.left())) : WIDTH, HEIGHT); + nd.position = WORKAREA.pos() + Vector2D(nextX, nextY); applyNodeDataToWindow(&nd); if (onRight) { @@ -637,6 +636,8 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) { PHLMONITOR PMONITOR = nullptr; + const auto WS = g_pCompositor->getWorkspaceByID(pNode->workspaceID); + if (g_pCompositor->isWorkspaceSpecial(pNode->workspaceID)) { for (auto const& m : g_pCompositor->m_monitors) { if (m->activeSpecialWorkspaceID() == pNode->workspaceID) { @@ -644,19 +645,20 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) { break; } } - } else - PMONITOR = g_pCompositor->getWorkspaceByID(pNode->workspaceID)->m_monitor.lock(); + } else if (WS) + PMONITOR = WS->m_monitor.lock(); - if (!PMONITOR) { + if (!PMONITOR || !WS) { Debug::log(ERR, "Orphaned Node {}!!", pNode); return; } // for gaps outer - const bool DISPLAYLEFT = STICKS(pNode->position.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x); - const bool DISPLAYRIGHT = STICKS(pNode->position.x + pNode->size.x, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x); - const bool DISPLAYTOP = STICKS(pNode->position.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y); - const bool DISPLAYBOTTOM = STICKS(pNode->position.y + pNode->size.y, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y); + const auto WORKAREA = workAreaOnWorkspace(WS); + const bool DISPLAYLEFT = STICKS(pNode->position.x, WORKAREA.x); + const bool DISPLAYRIGHT = STICKS(pNode->position.x + pNode->size.x, WORKAREA.x + WORKAREA.w); + const bool DISPLAYTOP = STICKS(pNode->position.y, WORKAREA.y); + const bool DISPLAYBOTTOM = STICKS(pNode->position.y + pNode->size.y, WORKAREA.y + WORKAREA.h); const auto PWINDOW = pNode->pWindow.lock(); // get specific gaps and rules for this workspace, @@ -669,14 +671,11 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) { PWINDOW->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT); PWINDOW->updateWindowData(); - static auto PANIMATE = CConfigValue("misc:animate_manual_resizes"); - static auto PGAPSINDATA = CConfigValue("general:gaps_in"); - static auto PGAPSOUTDATA = CConfigValue("general:gaps_out"); - auto* PGAPSIN = sc((PGAPSINDATA.ptr())->getData()); - auto* PGAPSOUT = sc((PGAPSOUTDATA.ptr())->getData()); + static auto PANIMATE = CConfigValue("misc:animate_manual_resizes"); + static auto PGAPSINDATA = CConfigValue("general:gaps_in"); + auto* PGAPSIN = sc((PGAPSINDATA.ptr())->getData()); - auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN); - auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT); + auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN); if (!validMapped(PWINDOW)) { Debug::log(ERR, "Node {} holding invalid {}!!", pNode, PWINDOW); @@ -691,9 +690,9 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) { auto calcPos = PWINDOW->m_position; auto calcSize = PWINDOW->m_size; - const auto OFFSETTOPLEFT = Vector2D(sc(DISPLAYLEFT ? gapsOut.m_left : gapsIn.m_left), sc(DISPLAYTOP ? gapsOut.m_top : gapsIn.m_top)); + const auto OFFSETTOPLEFT = Vector2D(sc(DISPLAYLEFT ? 0 : gapsIn.m_left), sc(DISPLAYTOP ? 0 : gapsIn.m_top)); - const auto OFFSETBOTTOMRIGHT = Vector2D(sc(DISPLAYRIGHT ? gapsOut.m_right : gapsIn.m_right), sc(DISPLAYBOTTOM ? gapsOut.m_bottom : gapsIn.m_bottom)); + const auto OFFSETBOTTOMRIGHT = Vector2D(sc(DISPLAYRIGHT ? 0 : gapsIn.m_right), sc(DISPLAYBOTTOM ? 0 : gapsIn.m_bottom)); calcPos = calcPos + OFFSETTOPLEFT; calcSize = calcSize - OFFSETTOPLEFT - OFFSETBOTTOMRIGHT; @@ -708,20 +707,17 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) { if (*PCLAMP_TILED) { const auto borderSize = PWINDOW->getRealBorderSize(); - Vector2D monitorAvailable = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight - - Vector2D{(double)(gapsOut.m_left + gapsOut.m_right), (double)(gapsOut.m_top + gapsOut.m_bottom)} - Vector2D{2.0 * borderSize, 2.0 * borderSize}; + Vector2D monitorAvailable = WORKAREA.size() - Vector2D{2.0 * borderSize, 2.0 * borderSize}; - Vector2D minSize = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable); - Vector2D maxSize = PWINDOW->isFullscreen() ? Vector2D{INFINITY, INFINITY} : - PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable); - calcSize = calcSize.clamp(minSize, maxSize); + Vector2D minSize = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable); + Vector2D maxSize = PWINDOW->isFullscreen() ? Vector2D{INFINITY, INFINITY} : + PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable); + calcSize = calcSize.clamp(minSize, maxSize); calcPos += (availableSpace - calcSize) / 2.0; - calcPos.x = std::clamp(calcPos.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x + gapsOut.m_left + borderSize, - PMONITOR->m_size.x + PMONITOR->m_position.x - PMONITOR->m_reservedBottomRight.x - gapsOut.m_right - calcSize.x - borderSize); - calcPos.y = std::clamp(calcPos.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y + gapsOut.m_top + borderSize, - PMONITOR->m_size.y + PMONITOR->m_position.y - PMONITOR->m_reservedBottomRight.y - gapsOut.m_bottom - calcSize.y - borderSize); + calcPos.x = std::clamp(calcPos.x, WORKAREA.x + borderSize, WORKAREA.x + WORKAREA.w - calcSize.x - borderSize); + calcPos.y = std::clamp(calcPos.y, WORKAREA.y + borderSize, WORKAREA.y + WORKAREA.h - calcSize.y - borderSize); } if (PWINDOW->onSpecialWorkspace() && !PWINDOW->isFullscreen()) { @@ -776,10 +772,11 @@ void CHyprMasterLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorne static auto SLAVECOUNTFORCENTER = CConfigValue("master:slave_count_for_center_master"); static auto PSMARTRESIZING = CConfigValue("master:smart_resizing"); - const bool DISPLAYBOTTOM = STICKS(PWINDOW->m_position.y + PWINDOW->m_size.y, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y); - const bool DISPLAYRIGHT = STICKS(PWINDOW->m_position.x + PWINDOW->m_size.x, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x); - const bool DISPLAYTOP = STICKS(PWINDOW->m_position.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y); - const bool DISPLAYLEFT = STICKS(PWINDOW->m_position.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x); + const auto WORKAREA = PMONITOR->logicalBoxMinusReserved(); + const bool DISPLAYBOTTOM = STICKS(PWINDOW->m_position.y + PWINDOW->m_size.y, WORKAREA.y + WORKAREA.h); + const bool DISPLAYRIGHT = STICKS(PWINDOW->m_position.x + PWINDOW->m_size.x, WORKAREA.x + WORKAREA.w); + const bool DISPLAYTOP = STICKS(PWINDOW->m_position.y, WORKAREA.y); + const bool DISPLAYLEFT = STICKS(PWINDOW->m_position.x, WORKAREA.x); const bool LEFT = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT; const bool TOP = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT; @@ -825,13 +822,12 @@ void CHyprMasterLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorne const bool isStackVertical = orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT || orientation == ORIENTATION_CENTER; const auto RESIZEDELTA = isStackVertical ? pixResize.y : pixResize.x; - const auto WSSIZE = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight; auto nodesInSameColumn = PNODE->isMaster ? MASTERS : STACKWINDOWS; if (orientation == ORIENTATION_CENTER && !PNODE->isMaster) nodesInSameColumn = DISPLAYRIGHT ? (nodesInSameColumn + 1) / 2 : nodesInSameColumn / 2; - const auto SIZE = isStackVertical ? WSSIZE.y / nodesInSameColumn : WSSIZE.x / nodesInSameColumn; + const auto SIZE = isStackVertical ? WORKAREA.h / nodesInSameColumn : WORKAREA.w / nodesInSameColumn; if (RESIZEDELTA != 0 && nodesInSameColumn > 1) { if (!*PSMARTRESIZING) { @@ -840,7 +836,7 @@ void CHyprMasterLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorne const auto NODEIT = std::ranges::find(m_masterNodesData, *PNODE); const auto REVNODEIT = std::ranges::find(m_masterNodesData | std::views::reverse, *PNODE); - const float totalSize = isStackVertical ? WSSIZE.y : WSSIZE.x; + const float totalSize = isStackVertical ? WORKAREA.h : WORKAREA.w; const float minSize = totalSize / nodesInSameColumn * 0.2; const bool resizePrevNodes = isStackVertical ? (TOP || DISPLAYBOTTOM) && !DISPLAYTOP : (LEFT || DISPLAYRIGHT) && !DISPLAYLEFT; @@ -936,9 +932,10 @@ void CHyprMasterLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFul // To keep consistent with the settings without C+P code SMasterNodeData fakeNode; + const auto WORKAREA = PMONITOR->logicalBoxMinusReserved(); fakeNode.pWindow = pWindow; - fakeNode.position = PMONITOR->m_position + PMONITOR->m_reservedTopLeft; - fakeNode.size = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight; + fakeNode.position = WORKAREA.pos(); + fakeNode.size = WORKAREA.size(); fakeNode.workspaceID = pWindow->workspaceID(); pWindow->m_position = fakeNode.position; pWindow->m_size = fakeNode.size; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 4423bbdb7d6..e66c73b9a67 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -1156,11 +1156,7 @@ SDispatchResult CKeybindManager::centerWindow(std::string args) { const auto PMONITOR = PWINDOW->m_monitor.lock(); - auto RESERVEDOFFSET = Vector2D(); - if (args == "1") - RESERVEDOFFSET = (PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight) / 2.f; - - *PWINDOW->m_realPosition = PMONITOR->middle() - PWINDOW->m_realSize->goal() / 2.f + RESERVEDOFFSET; + *PWINDOW->m_realPosition = PMONITOR->logicalBoxMinusReserved().middle() - PWINDOW->m_realSize->goal() / 2.f; PWINDOW->m_position = PWINDOW->m_realPosition->goal(); return {}; @@ -1670,15 +1666,15 @@ SDispatchResult CKeybindManager::moveActiveTo(std::string args) { PGAPSOUT = sc(PGAPSOUTDATA.ptr()->getData()); switch (arg) { - case 'l': vPosx = PMONITOR->m_reservedTopLeft.x + BORDERSIZE + PMONITOR->m_position.x + PGAPSOUT->m_left; break; + case 'l': vPosx = PMONITOR->m_reservedArea.left() + BORDERSIZE + PMONITOR->m_position.x + PGAPSOUT->m_left; break; case 'r': - vPosx = PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x - PLASTWINDOW->m_realSize->goal().x - BORDERSIZE + PMONITOR->m_position.x - PGAPSOUT->m_right; + vPosx = PMONITOR->m_size.x - PMONITOR->m_reservedArea.right() - PLASTWINDOW->m_realSize->goal().x - BORDERSIZE + PMONITOR->m_position.x - PGAPSOUT->m_right; break; case 't': - case 'u': vPosy = PMONITOR->m_reservedTopLeft.y + BORDERSIZE + PMONITOR->m_position.y + PGAPSOUT->m_top; break; + case 'u': vPosy = PMONITOR->m_reservedArea.top() + BORDERSIZE + PMONITOR->m_position.y + PGAPSOUT->m_top; break; case 'b': case 'd': - vPosy = PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y - PLASTWINDOW->m_realSize->goal().y - BORDERSIZE + PMONITOR->m_position.y - PGAPSOUT->m_bottom; + vPosy = PMONITOR->m_size.y - PMONITOR->m_reservedArea.bottom() - PLASTWINDOW->m_realSize->goal().y - BORDERSIZE + PMONITOR->m_position.y - PGAPSOUT->m_bottom; break; } diff --git a/src/managers/animation/DesktopAnimationManager.cpp b/src/managers/animation/DesktopAnimationManager.cpp index 8a340554438..a56aa2f916f 100644 --- a/src/managers/animation/DesktopAnimationManager.cpp +++ b/src/managers/animation/DesktopAnimationManager.cpp @@ -408,10 +408,11 @@ void CDesktopAnimationManager::animationSlide(PHLWINDOW pWindow, std::string for const auto MIDPOINT = GOALPOS + GOALSIZE / 2.f; // check sides it touches - const bool DISPLAYLEFT = STICKS(pWindow->m_position.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x); - const bool DISPLAYRIGHT = STICKS(pWindow->m_position.x + pWindow->m_size.x, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x); - const bool DISPLAYTOP = STICKS(pWindow->m_position.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y); - const bool DISPLAYBOTTOM = STICKS(pWindow->m_position.y + pWindow->m_size.y, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y); + const auto MONITOR_WORKAREA = PMONITOR->logicalBoxMinusReserved(); + const bool DISPLAYLEFT = STICKS(pWindow->m_position.x, MONITOR_WORKAREA.x); + const bool DISPLAYRIGHT = STICKS(pWindow->m_position.x + pWindow->m_size.x, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w); + const bool DISPLAYTOP = STICKS(pWindow->m_position.y, MONITOR_WORKAREA.y); + const bool DISPLAYBOTTOM = STICKS(pWindow->m_position.y + pWindow->m_size.y, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h); if (DISPLAYBOTTOM && DISPLAYTOP) { if (DISPLAYLEFT && DISPLAYRIGHT) { diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index c4c72c41bd6..96c2af0ce1f 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1883,30 +1883,16 @@ void CHyprRenderer::arrangeLayerArray(PHLMONITOR pMonitor, const std::vectorgetMonitorFromID(monitor); - - static auto BAR_POSITION = CConfigValue("debug:error_position"); + const auto PMONITOR = g_pCompositor->getMonitorFromID(monitor); if (!PMONITOR) return; // Reset the reserved - PMONITOR->m_reservedBottomRight = Vector2D(); - PMONITOR->m_reservedTopLeft = Vector2D(); - - CBox usableArea = {PMONITOR->m_position.x, PMONITOR->m_position.y, PMONITOR->m_size.x, PMONITOR->m_size.y}; + PMONITOR->m_reservedArea.resetType(Desktop::RESERVED_DYNAMIC_TYPE_LS); - if (g_pHyprError->active() && Desktop::focusState()->monitor() == PMONITOR->m_self) { - const auto HEIGHT = g_pHyprError->height(); - if (*BAR_POSITION == 0) { - PMONITOR->m_reservedTopLeft.y = HEIGHT; - usableArea.y += HEIGHT; - usableArea.h -= HEIGHT; - } else { - PMONITOR->m_reservedBottomRight.y = HEIGHT; - usableArea.h -= HEIGHT; - } - } + const CBox ORIGINAL_USABLE_AREA = PMONITOR->logicalBoxMinusReserved(); + CBox usableArea = ORIGINAL_USABLE_AREA; for (auto& la : PMONITOR->m_layerSurfaceLayers) { std::ranges::stable_sort( @@ -1919,18 +1905,7 @@ void CHyprRenderer::arrangeLayersForMonitor(const MONITORID& monitor) { for (auto const& la : PMONITOR->m_layerSurfaceLayers) arrangeLayerArray(PMONITOR, la, false, &usableArea); - PMONITOR->m_reservedTopLeft = Vector2D(usableArea.x, usableArea.y) - PMONITOR->m_position; - PMONITOR->m_reservedBottomRight = PMONITOR->m_size - Vector2D(usableArea.width, usableArea.height) - PMONITOR->m_reservedTopLeft; - - auto ADDITIONALRESERVED = g_pConfigManager->m_mAdditionalReservedAreas.find(PMONITOR->m_name); - if (ADDITIONALRESERVED == g_pConfigManager->m_mAdditionalReservedAreas.end()) { - ADDITIONALRESERVED = g_pConfigManager->m_mAdditionalReservedAreas.find(""); // glob wildcard - } - - if (ADDITIONALRESERVED != g_pConfigManager->m_mAdditionalReservedAreas.end()) { - PMONITOR->m_reservedTopLeft = PMONITOR->m_reservedTopLeft + Vector2D(ADDITIONALRESERVED->second.left, ADDITIONALRESERVED->second.top); - PMONITOR->m_reservedBottomRight = PMONITOR->m_reservedBottomRight + Vector2D(ADDITIONALRESERVED->second.right, ADDITIONALRESERVED->second.bottom); - } + PMONITOR->m_reservedArea.addType(Desktop::RESERVED_DYNAMIC_TYPE_LS, Desktop::CReservedArea{ORIGINAL_USABLE_AREA, usableArea}); // damage the monitor if can damageMonitor(PMONITOR); diff --git a/tests/desktop/Reserved.cpp b/tests/desktop/Reserved.cpp new file mode 100644 index 00000000000..8fbb7172c61 --- /dev/null +++ b/tests/desktop/Reserved.cpp @@ -0,0 +1,38 @@ + +#include + +#include + +TEST(Desktop, reservedArea) { + Desktop::CReservedArea a{{20, 30}, {40, 50}}; + CBox box = {1000, 1000, 1000, 1000}; + a.applyip(box); + + EXPECT_EQ(box.x, 1020); + EXPECT_EQ(box.y, 1030); + EXPECT_EQ(box.w, 1000 - 20 - 40); + EXPECT_EQ(box.h, 1000 - 30 - 50); + + box = a.apply(CBox{1000, 1000, 1000, 1000}); + + EXPECT_EQ(box.x, 1020); + EXPECT_EQ(box.y, 1030); + EXPECT_EQ(box.w, 1000 - 20 - 40); + EXPECT_EQ(box.h, 1000 - 30 - 50); + + a.addType(Desktop::RESERVED_DYNAMIC_TYPE_LS, {10, 20}, {30, 40}); + + box = a.apply(CBox{1000, 1000, 1000, 1000}); + + EXPECT_EQ(box.x, 1000 + 20 + 10); + EXPECT_EQ(box.y, 1000 + 30 + 20); + EXPECT_EQ(box.w, 1000 - 20 - 40 - 10 - 30); + EXPECT_EQ(box.h, 1000 - 30 - 50 - 20 - 40); + + Desktop::CReservedArea b{CBox{10, 10, 1000, 1000}, CBox{20, 30, 900, 900}}; + + EXPECT_EQ(b.left(), 20 - 10); + EXPECT_EQ(b.top(), 30 - 10); + EXPECT_EQ(b.right(), 1010 - 920); + EXPECT_EQ(b.bottom(), 1010 - 930); +} \ No newline at end of file