diff --git a/CMakeLists.txt b/CMakeLists.txt index 88e094a7..f894212c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,7 @@ ExternalProject_Add( PREFIX ${CMAKE_SOURCE_DIR}/subprojects/wlroots SOURCE_DIR ${CMAKE_SOURCE_DIR}/subprojects/wlroots PATCH_COMMAND sed -E -i -e "s/(soversion = .*$)/soversion = 13032/g" meson.build - CONFIGURE_COMMAND meson setup build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dexamples=false -Drenderers=gles2 $,-Db_sanitize=address,-Db_sanitize=none> && meson setup build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dexamples=false -Drenderers=gles2 $,-Db_sanitize=address,-Db_sanitize=none> --reconfigure + CONFIGURE_COMMAND meson setup --reconfigure build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dxwayland=$,disabled,enabled> -Dexamples=false -Drenderers=gles2 $,-Db_sanitize=address,-Db_sanitize=none> BUILD_COMMAND ninja -C build BUILD_ALWAYS true BUILD_IN_SOURCE true diff --git a/Makefile b/Makefile index e3224970..9c5e94d5 100644 --- a/Makefile +++ b/Makefile @@ -1,22 +1,22 @@ PREFIX = /usr/local legacyrenderer: - cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` chmod -R 777 ./build legacyrendererdebug: - cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` chmod -R 777 ./build release: - cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build -G Ninja cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` chmod -R 777 ./build debug: - cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -S . -B ./build -G Ninja + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build -G Ninja cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` chmod -R 777 ./build @@ -45,7 +45,7 @@ install: cd ${PREFIX}/bin && ln -sf Hyprland hyprland if [ ! -f ${PREFIX}/share/wayland-sessions/hyprland.desktop ]; then cp ./example/hyprland.desktop ${PREFIX}/share/wayland-sessions; fi mkdir -p ${PREFIX}/share/hyprland - cp ./assets/wall_* ${PREFIX}/share/hyprland + cp ./assets/wall* ${PREFIX}/share/hyprland mkdir -p ${PREFIX}/share/xdg-desktop-portal cp ./assets/hyprland-portals.conf ${PREFIX}/share/xdg-desktop-portal @@ -60,6 +60,7 @@ install: uninstall: rm -f ${PREFIX}/share/wayland-sessions/hyprland.desktop rm -f ${PREFIX}/bin/Hyprland + rm -f ${PREFIX}/bin/hyprland rm -f ${PREFIX}/bin/hyprctl rm -f ${PREFIX}/bin/hyprpm rm -f ${PREFIX}/lib/libwlroots.so.13032 diff --git a/assets/meson.build b/assets/meson.build index cdfafabb..8c4a60ec 100644 --- a/assets/meson.build +++ b/assets/meson.build @@ -1,9 +1,7 @@ -wallpaper_types = ['', 'anime_', 'anime2_'] +wallpapers = ['0', '1', '2'] -foreach type : wallpaper_types - foreach size : [2, 4, 8] - install_data(f'wall_@type@@size@K.png', install_dir: join_paths(get_option('datadir'), 'hyprland'), install_tag: 'runtime') - endforeach +foreach type : wallpapers + install_data(f'wall@type@.png', install_dir: join_paths(get_option('datadir'), 'hyprland'), install_tag: 'runtime') endforeach install_data('hyprland-portals.conf', install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'), install_tag: 'runtime') diff --git a/assets/wall0.png b/assets/wall0.png new file mode 100644 index 00000000..73bdeef8 Binary files /dev/null and b/assets/wall0.png differ diff --git a/assets/wall1.png b/assets/wall1.png new file mode 100644 index 00000000..ad2b5678 Binary files /dev/null and b/assets/wall1.png differ diff --git a/assets/wall2.png b/assets/wall2.png new file mode 100644 index 00000000..9ade4720 Binary files /dev/null and b/assets/wall2.png differ diff --git a/assets/wall_2K.png b/assets/wall_2K.png deleted file mode 100644 index 5aea012a..00000000 Binary files a/assets/wall_2K.png and /dev/null differ diff --git a/assets/wall_4K.png b/assets/wall_4K.png deleted file mode 100644 index f835a978..00000000 Binary files a/assets/wall_4K.png and /dev/null differ diff --git a/assets/wall_8K.png b/assets/wall_8K.png deleted file mode 100644 index 539aa97d..00000000 Binary files a/assets/wall_8K.png and /dev/null differ diff --git a/assets/wall_anime2_2K.png b/assets/wall_anime2_2K.png deleted file mode 100644 index 5a465efa..00000000 Binary files a/assets/wall_anime2_2K.png and /dev/null differ diff --git a/assets/wall_anime2_4K.png b/assets/wall_anime2_4K.png deleted file mode 100644 index b04e6d00..00000000 Binary files a/assets/wall_anime2_4K.png and /dev/null differ diff --git a/assets/wall_anime2_8K.png b/assets/wall_anime2_8K.png deleted file mode 100644 index b8da18ef..00000000 Binary files a/assets/wall_anime2_8K.png and /dev/null differ diff --git a/assets/wall_anime_2K.png b/assets/wall_anime_2K.png deleted file mode 100644 index 202dc493..00000000 Binary files a/assets/wall_anime_2K.png and /dev/null differ diff --git a/assets/wall_anime_4K.png b/assets/wall_anime_4K.png deleted file mode 100644 index 687b79c3..00000000 Binary files a/assets/wall_anime_4K.png and /dev/null differ diff --git a/assets/wall_anime_8K.png b/assets/wall_anime_8K.png deleted file mode 100644 index 25c15cd7..00000000 Binary files a/assets/wall_anime_8K.png and /dev/null differ diff --git a/example/hyprland.conf b/example/hyprland.conf index 62b6e6e5..5d5441e9 100644 --- a/example/hyprland.conf +++ b/example/hyprland.conf @@ -112,7 +112,7 @@ gestures { misc { # See https://wiki.hyprland.org/Configuring/Variables/ for more - force_default_wallpaper = -1 # Set to 0 to disable the anime mascot wallpapers + force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers } # Example per-device config diff --git a/flake.lock b/flake.lock index 1619129f..8af1695b 100644 --- a/flake.lock +++ b/flake.lock @@ -25,7 +25,10 @@ }, "hyprlang": { "inputs": { - "nixpkgs": "nixpkgs_2" + "nixpkgs": [ + "xdph", + "nixpkgs" + ] }, "locked": { "lastModified": 1704287638, @@ -43,27 +46,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1704194953, - "narHash": "sha256-RtDKd8Mynhe5CFnVT8s0/0yqtWFMM9LmCzXv/YKxnq4=", + "lastModified": 1706191920, + "narHash": "sha256-eLihrZAPZX0R6RyM5fYAWeKVNuQPYjAkCUBr+JNvtdE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "bd645e8668ec6612439a9ee7e71f7eac4099d4f6", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1702645756, - "narHash": "sha256-qKI6OR3TYJYQB3Q8mAZ+DG4o/BR9ptcv9UnRV2hzljc=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "40c3c94c241286dd2243ea34d3aef8a488f9e4d0", + "rev": "ae5c332cbb5827f6b1f02572496b141021de335f", "type": "github" }, "original": { @@ -101,18 +88,18 @@ "flake": false, "locked": { "host": "gitlab.freedesktop.org", - "lastModified": 1703963193, - "narHash": "sha256-ke8drv6PTrdQDruWbajrRJffP9A9PU6FRyjJGNZRTs4=", + "lastModified": 1706359063, + "narHash": "sha256-5HUTG0p+nCJv3cn73AmFHRZdfRV5AD5N43g8xAePSKM=", "owner": "wlroots", "repo": "wlroots", - "rev": "f81c3d93cd6f61b20ae784297679283438def8df", + "rev": "00b869c1a96f300a8f25da95d624524895e0ddf2", "type": "gitlab" }, "original": { "host": "gitlab.freedesktop.org", "owner": "wlroots", "repo": "wlroots", - "rev": "f81c3d93cd6f61b20ae784297679283438def8df", + "rev": "00b869c1a96f300a8f25da95d624524895e0ddf2", "type": "gitlab" } }, @@ -130,11 +117,11 @@ ] }, "locked": { - "lastModified": 1704400467, - "narHash": "sha256-IsEAKBCorRlN53FwFAMbyGLRsPVu/ZrWEJtCwykPds8=", + "lastModified": 1706145785, + "narHash": "sha256-j9MP4fv2U/vdRKAXXc2gyMTmYwVnHP6kHx1/y6jprrU=", "owner": "hyprwm", "repo": "xdg-desktop-portal-hyprland", - "rev": "1c802128f6cc3db29a8ef01552b1a22f894eeefd", + "rev": "5a592647587cd20b9692a347df6939b6d371b3bb", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 7f9a958b..32509954 100644 --- a/flake.nix +++ b/flake.nix @@ -12,7 +12,7 @@ host = "gitlab.freedesktop.org"; owner = "wlroots"; repo = "wlroots"; - rev = "f81c3d93cd6f61b20ae784297679283438def8df"; + rev = "00b869c1a96f300a8f25da95d624524895e0ddf2"; flake = false; }; @@ -86,6 +86,7 @@ name = "hyprland-shell"; nativeBuildInputs = with pkgsFor.${system}; [cmake python3]; buildInputs = [self.packages.${system}.wlroots-hyprland]; + hardeningDisable = [ "fortify" ]; inputsFrom = [ self.packages.${system}.wlroots-hyprland self.packages.${system}.hyprland diff --git a/hyprpm/src/core/DataState.cpp b/hyprpm/src/core/DataState.cpp index 724c40ac..b6b34375 100644 --- a/hyprpm/src/core/DataState.cpp +++ b/hyprpm/src/core/DataState.cpp @@ -20,11 +20,18 @@ std::string DataState::getDataStatePath() { return std::string{HOME} + "/.local/share/hyprpm"; } +std::string DataState::getHeadersPath() { + return getDataStatePath() + "/headersRoot"; +} + void DataState::ensureStateStoreExists() { const auto PATH = getDataStatePath(); if (!std::filesystem::exists(PATH)) std::filesystem::create_directories(PATH); + + if (!std::filesystem::exists(getHeadersPath())) + std::filesystem::create_directories(getHeadersPath()); } void DataState::addNewPluginRepo(const SPluginRepository& repo) { @@ -64,7 +71,10 @@ bool DataState::pluginRepoExists(const std::string& urlOrName) { const auto PATH = getDataStatePath(); for (const auto& entry : std::filesystem::directory_iterator(PATH)) { - if (!entry.is_directory()) + if (!entry.is_directory() || entry.path().stem() == "headersRoot") + continue; + + if (!std::filesystem::exists(entry.path().string() + "/state.toml")) continue; auto STATE = toml::parse_file(entry.path().string() + "/state.toml"); @@ -85,7 +95,10 @@ void DataState::removePluginRepo(const std::string& urlOrName) { const auto PATH = getDataStatePath(); for (const auto& entry : std::filesystem::directory_iterator(PATH)) { - if (!entry.is_directory()) + if (!entry.is_directory() || entry.path().stem() == "headersRoot") + continue; + + if (!std::filesystem::exists(entry.path().string() + "/state.toml")) continue; auto STATE = toml::parse_file(entry.path().string() + "/state.toml"); @@ -154,7 +167,10 @@ std::vector DataState::getAllRepositories() { std::vector repos; for (const auto& entry : std::filesystem::directory_iterator(PATH)) { - if (!entry.is_directory()) + if (!entry.is_directory() || entry.path().stem() == "headersRoot") + continue; + + if (!std::filesystem::exists(entry.path().string() + "/state.toml")) continue; auto STATE = toml::parse_file(entry.path().string() + "/state.toml"); @@ -191,7 +207,10 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) { const auto PATH = getDataStatePath(); for (const auto& entry : std::filesystem::directory_iterator(PATH)) { - if (!entry.is_directory()) + if (!entry.is_directory() || entry.path().stem() == "headersRoot") + continue; + + if (!std::filesystem::exists(entry.path().string() + "/state.toml")) continue; auto STATE = toml::parse_file(entry.path().string() + "/state.toml"); diff --git a/hyprpm/src/core/DataState.hpp b/hyprpm/src/core/DataState.hpp index ac81dae1..70788f72 100644 --- a/hyprpm/src/core/DataState.hpp +++ b/hyprpm/src/core/DataState.hpp @@ -10,6 +10,7 @@ struct SGlobalState { namespace DataState { std::string getDataStatePath(); + std::string getHeadersPath(); void ensureStateStoreExists(); void addNewPluginRepo(const SPluginRepository& repo); void removePluginRepo(const std::string& urlOrName); diff --git a/hyprpm/src/core/PluginManager.cpp b/hyprpm/src/core/PluginManager.cpp index 4cc93d4b..49e2a3bf 100644 --- a/hyprpm/src/core/PluginManager.cpp +++ b/hyprpm/src/core/PluginManager.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -78,6 +79,8 @@ SHyprlandVersion CPluginManager::getHyprlandVersion() { bool CPluginManager::addNewPluginRepo(const std::string& url) { + const auto HLVER = getHyprlandVersion(); + if (DataState::pluginRepoExists(url)) { std::cerr << "\n" << Colors::RED << "✖" << Colors::RESET << " Could not clone the plugin repository. Repository already installed.\n"; return false; @@ -170,6 +173,22 @@ bool CPluginManager::addNewPluginRepo(const std::string& url) { message += " version " + pl.version; progress.printMessageAbove(message); } + + if (!pManifest->m_sRepository.commitPins.empty()) { + // check commit pins + + progress.printMessageAbove(std::string{Colors::RESET} + " → Manifest has " + std::to_string(pManifest->m_sRepository.commitPins.size()) + " pins, checking"); + + for (auto& [hl, plugin] : pManifest->m_sRepository.commitPins) { + if (hl != HLVER.hash) + continue; + + progress.printMessageAbove(std::string{Colors::GREEN} + "✔" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting"); + + execAndGet("cd /tmp/hyprpm/new/ && git reset --hard --recurse-submodules " + plugin); + } + } + progress.m_szCurrentMessage = "Verifying headers"; progress.print(); @@ -191,7 +210,8 @@ bool CPluginManager::addNewPluginRepo(const std::string& url) { progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name); for (auto& bs : p.buildSteps) { - out += execAndGet("cd /tmp/hyprpm/new && " + bs) + "\n"; + std::string cmd = std::format("cd /tmp/hyprpm/new && PKG_CONFIG_PATH=\"{}\" {}", DataState::getHeadersPath(), bs); + out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n"; } if (!std::filesystem::exists("/tmp/hyprpm/new/" + p.output)) { @@ -265,8 +285,12 @@ bool CPluginManager::removePluginRepo(const std::string& urlOrName) { eHeadersErrors CPluginManager::headersValid() { const auto HLVER = getHyprlandVersion(); + if (!std::filesystem::exists(DataState::getHeadersPath() + "/share/pkgconfig/hyprland.pc")) + return HEADERS_MISSING; + // find headers commit - auto headers = execAndGet("pkg-config --cflags --keep-system-cflags hyprland"); + std::string cmd = std::format("PKG_CONFIG_PATH={}/share/pkgconfig pkg-config --cflags --keep-system-cflags hyprland", DataState::getHeadersPath()); + auto headers = execAndGet(cmd.c_str()); if (!headers.contains("-I/")) return HEADERS_MISSING; @@ -298,10 +322,6 @@ eHeadersErrors CPluginManager::headersValid() { if (!ifs.good()) return HEADERS_CORRUPTED; - if ((std::filesystem::exists("/usr/include/hyprland/src/version.h") && verHeader != "/usr/include/hyprland/src/version.h") || - (std::filesystem::exists("/usr/local/include/hyprland/src/version.h") && verHeader != "/usr/local/include/hyprland/src/version.h")) - return HEADERS_DUPLICATED; - std::string verHeaderContent((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); ifs.close(); @@ -316,7 +336,9 @@ eHeadersErrors CPluginManager::headersValid() { return HEADERS_OK; } -bool CPluginManager::updateHeaders() { +bool CPluginManager::updateHeaders(bool force) { + + DataState::ensureStateStoreExists(); const auto HLVER = getHyprlandVersion(); @@ -325,11 +347,8 @@ bool CPluginManager::updateHeaders() { std::filesystem::permissions("/tmp/hyprpm", std::filesystem::perms::all, std::filesystem::perm_options::replace); } - if (headersValid() == HEADERS_OK) { - std::cout << "\n" << std::string{Colors::GREEN} + "✔" + Colors::RESET + " Your headers are already up-to-date.\n"; - auto GLOBALSTATE = DataState::getGlobalState(); - GLOBALSTATE.headersHashCompiled = HLVER.hash; - DataState::updateGlobalState(GLOBALSTATE); + if (!force && headersValid() == HEADERS_OK) { + std::cout << "\n" << std::string{Colors::GREEN} + "✔" + Colors::RESET + " Headers up to date.\n"; return true; } @@ -371,23 +390,38 @@ bool CPluginManager::updateHeaders() { progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " configuring Hyprland"); - ret = execAndGet("cd /tmp/hyprpm/hyprland && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja"); + if (m_bVerbose) + progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "setting PREFIX for cmake to " + DataState::getHeadersPath()); + + ret = execAndGet( + std::format("cd /tmp/hyprpm/hyprland && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build -G Ninja", + DataState::getHeadersPath())); + if (m_bVerbose) + progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "cmake returned: " + ret); + // le hack. Wlroots has to generate its build/include ret = execAndGet("cd /tmp/hyprpm/hyprland/subprojects/wlroots && meson setup -Drenderers=gles2 -Dexamples=false build"); + if (m_bVerbose) + progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "meson returned: " + ret); progress.printMessageAbove(std::string{Colors::GREEN} + "✔" + Colors::RESET + " configured Hyprland"); progress.m_iSteps = 4; progress.m_szCurrentMessage = "Installing sources"; progress.print(); - progress.printMessageAbove( - std::string{Colors::YELLOW} + "!" + Colors::RESET + - " in order to install the sources, you will need to input your password.\n If nothing pops up, make sure you have polkit and an authentication daemon running."); + // progress.printMessageAbove( + // std::string{Colors::YELLOW} + "!" + Colors::RESET + + // " in order to install the sources, you will need to input your password.\n If nothing pops up, make sure you have polkit and an authentication daemon running."); - ret = execAndGet("pkexec sh \"-c\" \"cd /tmp/hyprpm/hyprland && make installheaders\""); + std::string cmd = std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" /tmp/hyprpm/hyprland/Makefile && cd /tmp/hyprpm/hyprland && make installheaders", + DataState::getHeadersPath()); + if (m_bVerbose) + progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "installation will run: " + cmd); + + ret = execAndGet(cmd); if (m_bVerbose) - std::cout << Colors::BLUE << "[v] " << Colors::RESET << "pkexec returned: " << ret << "\n"; + std::cout << Colors::BLUE << "[v] " << Colors::RESET << "installer returned: " << ret << "\n"; // remove build files std::filesystem::remove_all("/tmp/hyprpm/hyprland"); @@ -399,10 +433,6 @@ bool CPluginManager::updateHeaders() { progress.m_szCurrentMessage = "Done!"; progress.print(); - auto GLOBALSTATE = DataState::getGlobalState(); - GLOBALSTATE.headersHashCompiled = HLVER.hash; - DataState::updateGlobalState(GLOBALSTATE); - std::cout << "\n"; } else { progress.printMessageAbove(std::string{Colors::RED} + "✖" + Colors::RESET + " failed to install headers with error code " + std::to_string((int)HEADERSVALID)); @@ -436,7 +466,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { const auto HLVER = getHyprlandVersion(); CProgressBar progress; - progress.m_iMaxSteps = REPOS.size() * 2 + 1; + progress.m_iMaxSteps = REPOS.size() * 2 + 2; progress.m_iSteps = 0; progress.m_szCurrentMessage = "Updating repositories"; progress.print(); @@ -530,7 +560,8 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name); for (auto& bs : p.buildSteps) { - out += execAndGet("cd /tmp/hyprpm/update && " + bs) + "\n"; + std::string cmd = std::format("cd /tmp/hyprpm/update && PKG_CONFIG_PATH=\"{}\" {}", DataState::getHeadersPath(), bs); + out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n"; } if (!std::filesystem::exists("/tmp/hyprpm/update/" + p.output)) { @@ -568,6 +599,14 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { progress.printMessageAbove(std::string{Colors::GREEN} + "✔" + Colors::RESET + " updated " + repo.name); } + progress.m_iSteps++; + progress.m_szCurrentMessage = "Updating global state..."; + progress.print(); + + auto GLOBALSTATE = DataState::getGlobalState(); + GLOBALSTATE.headersHashCompiled = HLVER.hash; + DataState::updateGlobalState(GLOBALSTATE); + progress.m_iSteps++; progress.m_szCurrentMessage = "Done!"; progress.print(); diff --git a/hyprpm/src/core/PluginManager.hpp b/hyprpm/src/core/PluginManager.hpp index 3c5c7c5c..dedb2d43 100644 --- a/hyprpm/src/core/PluginManager.hpp +++ b/hyprpm/src/core/PluginManager.hpp @@ -40,7 +40,7 @@ class CPluginManager { bool removePluginRepo(const std::string& urlOrName); eHeadersErrors headersValid(); - bool updateHeaders(); + bool updateHeaders(bool force = false); bool updatePlugins(bool forceUpdateAll); void listAllPlugins(); diff --git a/hyprpm/src/main.cpp b/hyprpm/src/main.cpp index 7bc44141..37972443 100644 --- a/hyprpm/src/main.cpp +++ b/hyprpm/src/main.cpp @@ -1,6 +1,7 @@ #include "progress/CProgressBar.hpp" #include "helpers/Colors.hpp" #include "core/PluginManager.hpp" +#include "core/DataState.hpp" #include #include @@ -23,6 +24,7 @@ const std::string HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager ┣ --notify | -n → Send a hyprland notification for important events (e.g. load fail) ┣ --help | -h → Show this menu ┣ --verbose | -v → Enable too much logging +┣ --force | -f → Force an operation ignoring checks (e.g. update -f) ┗ )#"; @@ -38,7 +40,7 @@ int main(int argc, char** argv, char** envp) { } std::vector command; - bool notify = false, verbose = false; + bool notify = false, verbose = false, force = false; for (int i = 1; i < argc; ++i) { if (ARGS[i].starts_with("-")) { @@ -49,6 +51,9 @@ int main(int argc, char** argv, char** envp) { notify = true; } else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") { verbose = true; + } else if (ARGS[i] == "--force" || ARGS[i] == "-f") { + force = true; + std::cout << Colors::RED << "!" << Colors::RESET << " Using --force, I hope you know what you are doing.\n"; } else { std::cerr << "Unrecognized option " << ARGS[i]; return 1; @@ -82,9 +87,13 @@ int main(int argc, char** argv, char** envp) { return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1; } else if (command[0] == "update") { bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK; - bool headers = g_pPluginManager->updateHeaders(); + bool headers = g_pPluginManager->updateHeaders(force); if (headers) { - bool ret1 = g_pPluginManager->updatePlugins(!headersValid); + const auto HLVER = g_pPluginManager->getHyprlandVersion(); + auto GLOBALSTATE = DataState::getGlobalState(); + const auto COMPILEDOUTDATED = HLVER.hash != GLOBALSTATE.headersHashCompiled; + + bool ret1 = g_pPluginManager->updatePlugins(!headersValid || force || COMPILEDOUTDATED); if (!ret1) return 1; diff --git a/nix/overlays.nix b/nix/overlays.nix index 5eb7b082..afdde162 100644 --- a/nix/overlays.nix +++ b/nix/overlays.nix @@ -10,19 +10,16 @@ (builtins.substring 4 2 longDate) (builtins.substring 6 2 longDate) ]); - - mkJoinedOverlays = overlays: final: prev: - lib.foldl' (attrs: overlay: attrs // (overlay final prev)) {} overlays; in { # Contains what a user is most likely to care about: # Hyprland itself, XDPH and the Share Picker. - default = mkJoinedOverlays (with self.overlays; [ + default = lib.composeManyExtensions (with self.overlays; [ hyprland-packages hyprland-extras ]); # Packages for variations of Hyprland, dependencies included. - hyprland-packages = mkJoinedOverlays [ + hyprland-packages = lib.composeManyExtensions [ # Dependencies inputs.hyprland-protocols.overlays.default self.overlays.wlroots-hyprland @@ -34,7 +31,7 @@ in { hyprland = final.callPackage ./default.nix { stdenv = final.gcc13Stdenv; version = "${props.version}+date=${date}_${self.shortRev or "dirty"}"; - wlroots = final.wlroots-hyprland; + wlroots = prev.wlroots-hyprland; commit = self.rev or ""; inherit (final) udis86 hyprland-protocols; inherit date; @@ -59,7 +56,7 @@ in { # Packages for extra software recommended for usage with Hyprland, # including forked or patched packages for compatibility. - hyprland-extras = mkJoinedOverlays [ + hyprland-extras = lib.composeManyExtensions [ inputs.xdph.overlays.xdg-desktop-portal-hyprland ]; diff --git a/nix/update-wlroots.sh b/nix/update-wlroots.sh index addf2df2..01f5cd83 100755 --- a/nix/update-wlroots.sh +++ b/nix/update-wlroots.sh @@ -8,7 +8,7 @@ CRT_REV=$(rg rev flake.nix | awk '{ print substr($3, 2, 40) }') if [ "$SUB_REV" != "$CRT_REV" ]; then echo "Updating wlroots..." # update wlroots to submodule revision - sed -Ei "s/\w{40}/$SUB_REV/g" flake.nix subprojects/wlroots.wrap + sed -Ei "s/\w{40}/$SUB_REV/g" flake.nix nix flake lock echo "wlroots: $CRT_REV -> $SUB_REV" diff --git a/protocols/meson.build b/protocols/meson.build index cc111e98..857df50f 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -1,5 +1,5 @@ wayland_protos = dependency('wayland-protocols', - version: '>=1.25', + version: '>=1.32', fallback: 'wayland-protocols', default_options: ['tests=false'], ) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index b98c714d..db2e5f4a 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -81,26 +81,6 @@ CCompositor::CCompositor() { CCompositor::~CCompositor() { cleanup(); - g_pDecorationPositioner.reset(); - g_pPluginSystem.reset(); - g_pHyprNotificationOverlay.reset(); - g_pDebugOverlay.reset(); - g_pEventManager.reset(); - g_pSessionLockManager.reset(); - g_pProtocolManager.reset(); - g_pXWaylandManager.reset(); - g_pHyprRenderer.reset(); - g_pHyprOpenGL.reset(); - g_pInputManager.reset(); - g_pThreadManager.reset(); - g_pConfigManager.reset(); - g_pLayoutManager.reset(); - g_pHyprError.reset(); - g_pConfigManager.reset(); - g_pAnimationManager.reset(); - g_pKeybindManager.reset(); - g_pHookSystem.reset(); - g_pWatchdog.reset(); } void CCompositor::setRandomSplash() { @@ -135,10 +115,10 @@ void CCompositor::initServer() { else wlr_log_init(WLR_ERROR, Debug::wlrLog); - m_sWLRBackend = wlr_backend_autocreate(m_sWLDisplay, &m_sWLRSession); + m_sWLRBackend = wlr_backend_autocreate(m_sWLEventLoop, &m_sWLRSession); if (!m_sWLRBackend) { - Debug::log(CRIT, "m_sWLRBackend was NULL!"); + Debug::log(CRIT, "m_sWLRBackend was NULL! This usually means wlroots could not find a GPU or enountered some issues."); throwError("wlr_backend_autocreate() failed!"); } @@ -151,7 +131,7 @@ void CCompositor::initServer() { m_sWLRRenderer = wlr_gles2_renderer_create_with_drm_fd(m_iDRMFD); if (!m_sWLRRenderer) { - Debug::log(CRIT, "m_sWLRRenderer was NULL!"); + Debug::log(CRIT, "m_sWLRRenderer was NULL! This usually means wlroots could not find a GPU or enountered some issues."); throwError("wlr_gles2_renderer_create_with_drm_fd() failed!"); } @@ -259,7 +239,7 @@ void CCompositor::initServer() { m_sWLRActivation = wlr_xdg_activation_v1_create(m_sWLDisplay); - m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLDisplay); + m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLEventLoop); m_sWLRSessionLockMgr = wlr_session_lock_manager_v1_create(m_sWLDisplay); @@ -332,6 +312,59 @@ void CCompositor::initAllSignals() { addWLSignal(&m_sWLRSession->events.active, &Events::listen_sessionActive, m_sWLRSession, "Session"); } +void CCompositor::removeAllSignals() { + removeWLSignal(&Events::listen_newOutput); + removeWLSignal(&Events::listen_newXDGToplevel); + removeWLSignal(&Events::listen_mouseMove); + removeWLSignal(&Events::listen_mouseMoveAbsolute); + removeWLSignal(&Events::listen_mouseButton); + removeWLSignal(&Events::listen_mouseAxis); + removeWLSignal(&Events::listen_mouseFrame); + removeWLSignal(&Events::listen_swipeBegin); + removeWLSignal(&Events::listen_swipeUpdate); + removeWLSignal(&Events::listen_swipeEnd); + removeWLSignal(&Events::listen_pinchBegin); + removeWLSignal(&Events::listen_pinchUpdate); + removeWLSignal(&Events::listen_pinchEnd); + removeWLSignal(&Events::listen_touchBegin); + removeWLSignal(&Events::listen_touchEnd); + removeWLSignal(&Events::listen_touchUpdate); + removeWLSignal(&Events::listen_touchFrame); + removeWLSignal(&Events::listen_holdBegin); + removeWLSignal(&Events::listen_holdEnd); + removeWLSignal(&Events::listen_newInput); + removeWLSignal(&Events::listen_requestMouse); + removeWLSignal(&Events::listen_requestSetSel); + removeWLSignal(&Events::listen_requestDrag); + removeWLSignal(&Events::listen_startDrag); + removeWLSignal(&Events::listen_requestSetSel); + removeWLSignal(&Events::listen_requestSetPrimarySel); + removeWLSignal(&Events::listen_newLayerSurface); + removeWLSignal(&Events::listen_change); + removeWLSignal(&Events::listen_outputMgrApply); + removeWLSignal(&Events::listen_outputMgrTest); + removeWLSignal(&Events::listen_newConstraint); + removeWLSignal(&Events::listen_NewXDGDeco); + removeWLSignal(&Events::listen_newVirtPtr); + removeWLSignal(&Events::listen_newVirtualKeyboard); + removeWLSignal(&Events::listen_RendererDestroy); + removeWLSignal(&Events::listen_newIdleInhibitor); + removeWLSignal(&Events::listen_powerMgrSetMode); + removeWLSignal(&Events::listen_newIME); + removeWLSignal(&Events::listen_newTextInput); + removeWLSignal(&Events::listen_activateXDG); + removeWLSignal(&Events::listen_newSessionLock); + removeWLSignal(&Events::listen_setGamma); + removeWLSignal(&Events::listen_setCursorShape); + removeWLSignal(&Events::listen_newTearingHint); + + if (m_sWRLDRMLeaseMgr) + removeWLSignal(&Events::listen_leaseRequest); + + if (m_sWLRSession) + removeWLSignal(&Events::listen_sessionActive); +} + void CCompositor::cleanup() { if (!m_sWLDisplay || m_bIsShuttingDown) return; @@ -362,8 +395,8 @@ void CCompositor::cleanup() { for (auto& m : m_vMonitors) { g_pHyprOpenGL->destroyMonitorResources(m.get()); - wlr_output_enable(m->output, false); - wlr_output_commit(m->output); + wlr_output_state_set_enabled(m->state.wlr(), false); + m->state.commit(); } m_vMonitors.clear(); @@ -373,8 +406,31 @@ void CCompositor::cleanup() { g_pXWaylandManager->m_sWLRXWayland = nullptr; } + removeAllSignals(); + wl_display_destroy_clients(g_pCompositor->m_sWLDisplay); + g_pDecorationPositioner.reset(); + g_pPluginSystem.reset(); + g_pHyprNotificationOverlay.reset(); + g_pDebugOverlay.reset(); + g_pEventManager.reset(); + g_pSessionLockManager.reset(); + g_pProtocolManager.reset(); + g_pHyprRenderer.reset(); + g_pHyprOpenGL.reset(); + g_pInputManager.reset(); + g_pThreadManager.reset(); + g_pConfigManager.reset(); + g_pLayoutManager.reset(); + g_pHyprError.reset(); + g_pConfigManager.reset(); + g_pAnimationManager.reset(); + g_pKeybindManager.reset(); + g_pHookSystem.reset(); + g_pWatchdog.reset(); + g_pXWaylandManager.reset(); + wl_display_terminate(m_sWLDisplay); m_sWLDisplay = nullptr; @@ -654,6 +710,7 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreW static auto* const PRESIZEONBORDER = &g_pConfigManager->getConfigValuePtr("general:resize_on_border")->intValue; static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue; static auto* const PBORDERGRABEXTEND = &g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area")->intValue; + static auto* const PSPECIALFALLTHRU = &g_pConfigManager->getConfigValuePtr("input:special_fallthrough")->intValue; const auto BORDER_GRAB_AREA = *PRESIZEONBORDER ? *PBORDERSIZE + *PBORDERGRABEXTEND : 0; // pinned windows on top of floating regardless @@ -735,9 +792,16 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreW }; // special workspace - if (PMONITOR->specialWorkspaceID) + if (PMONITOR->specialWorkspaceID && !*PSPECIALFALLTHRU) return windowForWorkspace(true); + if (PMONITOR->specialWorkspaceID) { + const auto PWINDOW = windowForWorkspace(true); + + if (PWINDOW) + return PWINDOW; + } + return windowForWorkspace(false); } @@ -876,13 +940,19 @@ CMonitor* CCompositor::getMonitorFromOutput(wlr_output* out) { void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) { - static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; + static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; + static auto* const PSPECIALFALLTHROUGH = &g_pConfigManager->getConfigValuePtr("input:special_fallthrough")->intValue; if (g_pCompositor->m_sSeat.exclusiveClient) { Debug::log(LOG, "Disallowing setting focus to a window due to there being an active input inhibitor layer."); return; } + if (!g_pInputManager->m_dExclusiveLSes.empty()) { + Debug::log(LOG, "Refusing a keyboard focus to a window because of an exclusive ls"); + return; + } + g_pLayoutManager->getCurrentLayout()->bringWindowToTop(pWindow); if (!pWindow || !windowValidMapped(pWindow)) { @@ -943,7 +1013,9 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) { const auto PLASTWINDOW = m_pLastWindow; m_pLastWindow = pWindow; - if (PMONITOR->specialWorkspaceID && PMONITOR->specialWorkspaceID != pWindow->m_iWorkspaceID) + /* If special fallthrough is enabled, this behavior will be disabled, as I have no better idea of nicely tracking which + window focuses are "via keybinds" and which ones aren't. */ + if (PMONITOR->specialWorkspaceID && PMONITOR->specialWorkspaceID != pWindow->m_iWorkspaceID && !pWindow->m_bPinned && !*PSPECIALFALLTHROUGH) PMONITOR->setSpecialWorkspace(nullptr); // we need to make the PLASTWINDOW not equal to m_pLastWindow so that RENDERDATA is correct for an unfocused window @@ -1091,14 +1163,6 @@ wlr_surface* CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector< auto SURFACEAT = wlr_layer_surface_v1_surface_at(ls->layerSurface, pos.x - ls->geometry.x, pos.y - ls->geometry.y, &sCoords->x, &sCoords->y); - if (ls->layerSurface->current.keyboard_interactive && ls->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { - if (!SURFACEAT) - SURFACEAT = ls->layerSurface->surface; - - *ppLayerSurfaceFound = ls.get(); - return SURFACEAT; - } - if (SURFACEAT) { if (!pixman_region32_not_empty(&SURFACEAT->input_region)) continue; @@ -1881,15 +1945,13 @@ void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) { if (pWindow == m_pLastWindow) { const auto* const ACTIVECOLOR = !pWindow->m_sGroupData.pNextWindow ? (!pWindow->m_sGroupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL); - setBorderColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying() >= 0 ? - CGradientValueData(CColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying())) : - *ACTIVECOLOR); + setBorderColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying().m_vColors.empty() ? *ACTIVECOLOR : + pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying()); } else { const auto* const INACTIVECOLOR = !pWindow->m_sGroupData.pNextWindow ? (!pWindow->m_sGroupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) : (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL); - setBorderColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying() >= 0 ? - CGradientValueData(CColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying())) : - *INACTIVECOLOR); + setBorderColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying().m_vColors.empty() ? *INACTIVECOLOR : + pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying()); } } @@ -2113,7 +2175,7 @@ CMonitor* CCompositor::getMonitorFromString(const std::string& name) { return nullptr; } -void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMonitor) { +void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMonitor, bool noWarpCursor) { // We trust the workspace and monitor to be correct. @@ -2199,7 +2261,8 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni pWorkspace->startAnim(true, true, true); - wlr_cursor_warp(m_sWLRCursor, nullptr, pMonitor->vecPosition.x + pMonitor->vecTransformedSize.x / 2, pMonitor->vecPosition.y + pMonitor->vecTransformedSize.y / 2); + if (!noWarpCursor) + wlr_cursor_warp(m_sWLRCursor, nullptr, pMonitor->vecPosition.x + pMonitor->vecTransformedSize.x / 2, pMonitor->vecPosition.y + pMonitor->vecTransformedSize.y / 2); g_pInputManager->sendMotionEventsToFocused(); } @@ -2607,11 +2670,7 @@ int CCompositor::getNewSpecialID() { } void CCompositor::performUserChecks() { - if (g_pConfigManager->getInt("general:allow_tearing") == 1 && !envEnabled("WLR_DRM_NO_ATOMIC")) { - g_pHyprNotificationOverlay->addNotification("You have enabled tearing, but immediate presentations are not available on your configuration. Try adding " - "env = WLR_DRM_NO_ATOMIC,1 to your config.", - CColor(0), 15000, ICON_WARNING); - } + ; // intentional } void CCompositor::moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorkspace) { @@ -2786,10 +2845,27 @@ void CCompositor::leaveUnsafeState() { void CCompositor::setPreferredScaleForSurface(wlr_surface* pSurface, double scale) { g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(pSurface, scale); wlr_surface_set_preferred_buffer_scale(pSurface, static_cast(std::ceil(scale))); + + const auto PSURFACE = CWLSurface::surfaceFromWlr(pSurface); + if (!PSURFACE) { + Debug::log(WARN, "Orphaned wlr_surface {:x} in setPreferredScaleForSurface", (uintptr_t)pSurface); + return; + } + + PSURFACE->m_fLastScale = scale; + PSURFACE->m_iLastScale = static_cast(std::ceil(scale)); } void CCompositor::setPreferredTransformForSurface(wlr_surface* pSurface, wl_output_transform transform) { wlr_surface_set_preferred_buffer_transform(pSurface, transform); + + const auto PSURFACE = CWLSurface::surfaceFromWlr(pSurface); + if (!PSURFACE) { + Debug::log(WARN, "Orphaned wlr_surface {:x} in setPreferredTransformForSurface", (uintptr_t)pSurface); + return; + } + + PSURFACE->m_eLastTransform = transform; } void CCompositor::updateSuspendedStates() { diff --git a/src/Compositor.hpp b/src/Compositor.hpp index f954526c..9372839a 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -176,7 +176,7 @@ class CCompositor { void updateWorkspaceWindows(const int64_t& id); void updateWindowAnimatedDecorationValues(CWindow*); int getNextAvailableMonitorID(std::string const& name); - void moveWorkspaceToMonitor(CWorkspace*, CMonitor*); + void moveWorkspaceToMonitor(CWorkspace*, CMonitor*, bool noWarpCursor = false); void swapActiveWorkspaces(CMonitor*, CMonitor*); CMonitor* getMonitorFromString(const std::string&); bool workspaceIDOutOfBounds(const int64_t&); @@ -214,6 +214,7 @@ class CCompositor { private: void initAllSignals(); + void removeAllSignals(); void setRandomSplash(); void initManagers(eManagersInitStage stage); void prepareFallbackOutput(); diff --git a/src/Window.cpp b/src/Window.cpp index 13885409..78ac9bec 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -318,7 +318,7 @@ void CWindow::destroyToplevelHandle() { } void CWindow::updateToplevel() { - updateSurfaceOutputs(); + updateSurfaceScaleTransformDetails(); if (!m_phForeignToplevel) return; @@ -345,8 +345,8 @@ void sendLeaveIter(wlr_surface* pSurface, int x, int y, void* data) { wlr_surface_send_leave(pSurface, OUTPUT); } -void CWindow::updateSurfaceOutputs() { - if (m_iLastSurfaceMonitorID == m_iMonitorID || !m_bIsMapped || m_bHidden) +void CWindow::updateSurfaceScaleTransformDetails() { + if (!m_bIsMapped || m_bHidden) return; const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(m_iLastSurfaceMonitorID); @@ -355,16 +355,23 @@ void CWindow::updateSurfaceOutputs() { const auto PNEWMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); - if (PLASTMONITOR && PLASTMONITOR->m_bEnabled) - wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendLeaveIter, PLASTMONITOR->output); + if (PNEWMONITOR != PLASTMONITOR) { + if (PLASTMONITOR && PLASTMONITOR->m_bEnabled) + wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendLeaveIter, PLASTMONITOR->output); - wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendEnterIter, PNEWMONITOR->output); + wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendEnterIter, PNEWMONITOR->output); + } wlr_surface_for_each_surface( m_pWLSurface.wlr(), [](wlr_surface* surf, int x, int y, void* data) { const auto PMONITOR = g_pCompositor->getMonitorFromID(((CWindow*)data)->m_iMonitorID); - g_pCompositor->setPreferredScaleForSurface(surf, PMONITOR ? PMONITOR->scale : 1.f); + + const auto PSURFACE = CWLSurface::surfaceFromWlr(surf); + if (PSURFACE && PSURFACE->m_fLastScale == PMONITOR->scale) + return; + + g_pCompositor->setPreferredScaleForSurface(surf, PMONITOR->scale); g_pCompositor->setPreferredTransformForSurface(surf, PMONITOR->transform); }, this); @@ -511,6 +518,14 @@ void CWindow::onMap() { "CWindow"); m_vReportedSize = m_vPendingReportedSize; + + for (const auto& ctrl : g_pHyprRenderer->m_vTearingControllers) { + if (ctrl->pWlrHint->surface != m_pWLSurface.wlr()) + continue; + + m_bTearingHint = ctrl->pWlrHint->current; + break; + } } void CWindow::onBorderAngleAnimEnd(void* ptr) { @@ -607,14 +622,42 @@ void CWindow::applyDynamicRule(const SWindowRule& r) { m_sAdditionalConfigData.animationStyle = STYLE; } else if (r.szRule.starts_with("bordercolor")) { try { - std::string colorPart = removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find_first_of(' ') + 1)); + // Each vector will only get used if it has at least one color + CGradientValueData activeBorderGradient = {}; + CGradientValueData inactiveBorderGradient = {}; + bool active = true; + CVarList colorsAndAngles = CVarList(removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find_first_of(' ') + 1)), 0, 's', true); - if (colorPart.contains(' ')) { - // we have a space, 2 values - m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart.substr(0, colorPart.find_first_of(' '))); - m_sSpecialRenderData.inactiveBorderColor = configStringToInt(colorPart.substr(colorPart.find_first_of(' ') + 1)); - } else { - m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart); + // Basic form has only two colors, everything else can be parsed as a gradient + if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) { + m_sSpecialRenderData.activeBorderColor = CGradientValueData(CColor(configStringToInt(colorsAndAngles[0]))); + m_sSpecialRenderData.inactiveBorderColor = CGradientValueData(CColor(configStringToInt(colorsAndAngles[1]))); + return; + } + + for (auto& token : colorsAndAngles) { + // The first angle, or an explicit "0deg", splits the two gradients + if (active && token.contains("deg")) { + activeBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); + active = false; + } else if (token.contains("deg")) + inactiveBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); + else if (active) + activeBorderGradient.m_vColors.push_back(configStringToInt(token)); + else + inactiveBorderGradient.m_vColors.push_back(configStringToInt(token)); + } + + // Includes sanity checks for the number of colors in each gradient + if (activeBorderGradient.m_vColors.size() > 10 || inactiveBorderGradient.m_vColors.size() > 10) + Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", r.szRule); + else if (activeBorderGradient.m_vColors.empty()) + Debug::log(WARN, "Bordercolor rule \"{}\" has no colors, ignoring", r.szRule); + else if (inactiveBorderGradient.m_vColors.empty()) + m_sSpecialRenderData.activeBorderColor = activeBorderGradient; + else { + m_sSpecialRenderData.activeBorderColor = activeBorderGradient; + m_sSpecialRenderData.inactiveBorderColor = inactiveBorderGradient; } } catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r.szRule, e.what()); } } else if (r.szRule == "dimaround") { @@ -644,8 +687,8 @@ void CWindow::applyDynamicRule(const SWindowRule& r) { } void CWindow::updateDynamicRules() { - m_sSpecialRenderData.activeBorderColor = -1; - m_sSpecialRenderData.inactiveBorderColor = -1; + m_sSpecialRenderData.activeBorderColor = CGradientValueData(); + m_sSpecialRenderData.inactiveBorderColor = CGradientValueData(); m_sSpecialRenderData.alpha = 1.f; m_sSpecialRenderData.alphaInactive = -1.f; m_sAdditionalConfigData.forceNoBlur = false; @@ -1028,7 +1071,7 @@ int CWindow::getRealBorderSize() { } bool CWindow::canBeTorn() { - return (m_sAdditionalConfigData.forceTearing.toUnderlying() || m_bTearingHint) && g_pHyprRenderer->m_bTearingEnvSatisfied; + return (m_sAdditionalConfigData.forceTearing.toUnderlying() || m_bTearingHint); } bool CWindow::shouldSendFullscreenState() { diff --git a/src/Window.hpp b/src/Window.hpp index c87caa3c..0861c1db 100644 --- a/src/Window.hpp +++ b/src/Window.hpp @@ -104,13 +104,13 @@ class CWindowOverridableVar { }; struct SWindowSpecialRenderData { - CWindowOverridableVar alphaOverride = false; - CWindowOverridableVar alpha = 1.f; - CWindowOverridableVar alphaInactiveOverride = false; - CWindowOverridableVar alphaInactive = -1.f; // -1 means unset + CWindowOverridableVar alphaOverride = false; + CWindowOverridableVar alpha = 1.f; + CWindowOverridableVar alphaInactiveOverride = false; + CWindowOverridableVar alphaInactive = -1.f; // -1 means unset - CWindowOverridableVar activeBorderColor = -1; // -1 means unset - CWindowOverridableVar inactiveBorderColor = -1; // -1 means unset + CWindowOverridableVar activeBorderColor = CGradientValueData(); // empty color vector means unset + CWindowOverridableVar inactiveBorderColor = CGradientValueData(); // empty color vector means unset // set by the layout CWindowOverridableVar borderSize = -1; // -1 means unset @@ -354,7 +354,7 @@ class CWindow { void createToplevelHandle(); void destroyToplevelHandle(); void updateToplevel(); - void updateSurfaceOutputs(); + void updateSurfaceScaleTransformDetails(); void moveToWorkspace(int); CWindow* X11TransientFor(); void onUnmap(); diff --git a/src/config/ConfigDataValues.hpp b/src/config/ConfigDataValues.hpp index 914df58f..671b7c1d 100644 --- a/src/config/ConfigDataValues.hpp +++ b/src/config/ConfigDataValues.hpp @@ -17,6 +17,7 @@ class ICustomConfigValueData { class CGradientValueData : public ICustomConfigValueData { public: + CGradientValueData(){}; CGradientValueData(CColor col) { m_vColors.push_back(col); }; diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 075c64ca..44d15264 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -101,7 +101,6 @@ void CConfigManager::setDefaultVars() { configValues["misc:disable_hyprland_logo"].intValue = 0; configValues["misc:disable_splash_rendering"].intValue = 0; - configValues["misc:force_hypr_chan"].intValue = 0; configValues["misc:force_default_wallpaper"].intValue = -1; configValues["misc:vfr"].intValue = 1; configValues["misc:vrr"].intValue = 0; @@ -140,6 +139,7 @@ void CConfigManager::setDefaultVars() { configValues["group:groupbar:font_family"].strValue = "Sans"; configValues["group:groupbar:font_size"].intValue = 8; configValues["group:groupbar:gradients"].intValue = 1; + configValues["group:groupbar:height"].intValue = 14; configValues["group:groupbar:priority"].intValue = 3; configValues["group:groupbar:render_titles"].intValue = 1; configValues["group:groupbar:scrolling"].intValue = 1; @@ -225,6 +225,7 @@ void CConfigManager::setDefaultVars() { configValues["input:follow_mouse"].intValue = 1; configValues["input:mouse_refocus"].intValue = 1; + configValues["input:special_fallthrough"].intValue = 0; configValues["input:sensitivity"].floatValue = 0.f; configValues["input:accel_profile"].strValue = STRVAL_EMPTY; configValues["input:kb_file"].strValue = STRVAL_EMPTY; @@ -255,6 +256,7 @@ void CConfigManager::setDefaultVars() { configValues["input:touchpad:scroll_factor"].floatValue = 1.f; configValues["input:touchdevice:transform"].intValue = 0; configValues["input:touchdevice:output"].strValue = STRVAL_EMPTY; + configValues["input:touchdevice:enabled"].intValue = 1; configValues["input:tablet:transform"].intValue = 0; configValues["input:tablet:output"].strValue = STRVAL_EMPTY; configValues["input:tablet:region_position"].vecValue = Vector2D(); @@ -320,7 +322,7 @@ void CConfigManager::setDeviceDefaultVars(const std::string& dev) { cfgValues["scroll_points"].strValue = STRVAL_EMPTY; cfgValues["transform"].intValue = 0; cfgValues["output"].strValue = STRVAL_EMPTY; - cfgValues["enabled"].intValue = 1; // only for mice / touchpads + cfgValues["enabled"].intValue = 1; // only for mice, touchpads, and touchdevices cfgValues["region_position"].vecValue = Vector2D(); // only for tablets cfgValues["region_size"].vecValue = Vector2D(); // only for tablets cfgValues["relative_input"].intValue = 0; // only for tablets @@ -563,7 +565,7 @@ void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::s } } - if (COMMAND == "decoration:screen_shader") { + if (COMMAND == "decoration:screen_shader" && VALUE != STRVAL_EMPTY) { const auto PATH = absolutePath(VALUE, configCurrentPath); configPaths.push_back(PATH); @@ -2321,30 +2323,30 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) { static auto* const PVRR = &getConfigValuePtr("misc:vrr")->intValue; static auto ensureVRRForDisplay = [&](CMonitor* m) -> void { - if (!m->output) + if (!m->output || m->createdByUser) return; const auto USEVRR = m->activeMonitorRule.vrr.has_value() ? m->activeMonitorRule.vrr.value() : *PVRR; if (USEVRR == 0) { if (m->vrrActive) { - wlr_output_enable_adaptive_sync(m->output, 0); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); - if (!wlr_output_commit(m->output)) + if (!m->state.commit()) Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name); } m->vrrActive = false; return; } else if (USEVRR == 1) { if (!m->vrrActive) { - wlr_output_enable_adaptive_sync(m->output, 1); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 1); - if (!wlr_output_test(m->output)) { + if (!m->state.test()) { Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name); - wlr_output_enable_adaptive_sync(m->output, 0); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); } - if (!wlr_output_commit(m->output)) + if (!m->state.commit()) Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name); } m->vrrActive = true; @@ -2361,20 +2363,20 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) { const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL; if (WORKSPACEFULL && m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED) { - wlr_output_enable_adaptive_sync(m->output, 1); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 1); - if (!wlr_output_test(m->output)) { + if (!m->state.test()) { Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name); - wlr_output_enable_adaptive_sync(m->output, 0); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); } - if (!wlr_output_commit(m->output)) + if (!m->state.commit()) Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name); } else if (!WORKSPACEFULL && m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED) { - wlr_output_enable_adaptive_sync(m->output, 0); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); - if (!wlr_output_commit(m->output)) + if (!m->state.commit()) Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name); } } diff --git a/src/config/defaultConfig.hpp b/src/config/defaultConfig.hpp index 1e149a7a..7bd43bfd 100644 --- a/src/config/defaultConfig.hpp +++ b/src/config/defaultConfig.hpp @@ -119,7 +119,7 @@ gestures { misc { # See https://wiki.hyprland.org/Configuring/Variables/ for more - force_default_wallpaper = -1 # Set to 0 to disable the anime mascot wallpapers + force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers } # Example per-device config diff --git a/src/debug/CrashReporter.cpp b/src/debug/CrashReporter.cpp index 65b31399..5bc66286 100644 --- a/src/debug/CrashReporter.cpp +++ b/src/debug/CrashReporter.cpp @@ -119,7 +119,7 @@ void CrashReporter::createAndSaveCrash(int sig) { finalCrashReport += "\n\nLog tail:\n"; - finalCrashReport += Debug::rollingLog; + finalCrashReport += Debug::rollingLog.substr(Debug::rollingLog.find("\n") + 1); const auto HOME = getenv("HOME"); const auto CACHE_HOME = getenv("XDG_CACHE_HOME"); diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 03532de2..9d5886ec 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -1138,9 +1138,9 @@ std::string dispatchSetProp(std::string request) { } else if (PROP == "alphainactive") { PWINDOW->m_sSpecialRenderData.alphaInactive.forceSetIgnoreLocked(std::stof(VAL), lock); } else if (PROP == "activebordercolor") { - PWINDOW->m_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock); + PWINDOW->m_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(CGradientValueData(CColor(configStringToInt(VAL))), lock); } else if (PROP == "inactivebordercolor") { - PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock); + PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(CGradientValueData(CColor(configStringToInt(VAL))), lock); } else if (PROP == "forcergbx") { PWINDOW->m_sAdditionalConfigData.forceRGBX.forceSetIgnoreLocked(configStringToInt(VAL), lock); } else if (PROP == "bordersize") { @@ -1402,13 +1402,20 @@ std::string getReply(std::string request) { auto format = HyprCtl::FORMAT_NORMAL; // process flags for non-batch requests - if (!request.contains("[[BATCH]]") && request.contains("/")) { + if (!request.starts_with("[[BATCH]]") && request.contains("/")) { long unsigned int sepIndex = 0; for (const auto& c : request) { if (c == '/') { // stop at separator break; } + // after whitespace assume the first word as a keyword, + // so its value can have slashes (e.g., a path) + if (c == ' ') { + sepIndex = request.size(); + break; + } + sepIndex++; if (c == 'j') diff --git a/src/events/Layers.cpp b/src/events/Layers.cpp index d6e21d9d..9ef9b025 100644 --- a/src/events/Layers.cpp +++ b/src/events/Layers.cpp @@ -142,6 +142,9 @@ void Events::listener_mapLayerSurface(void* owner, void* data) { wlr_surface_send_enter(layersurface->layerSurface->surface, layersurface->layerSurface->output); + if (layersurface->layerSurface->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) + g_pInputManager->m_dExclusiveLSes.push_back(layersurface); + const bool GRABSFOCUS = layersurface->layerSurface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE && // don't focus if constrained (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint); @@ -183,6 +186,11 @@ void Events::listener_unmapLayerSurface(void* owner, void* data) { g_pEventManager->postEvent(SHyprIPCEvent{"closelayer", std::string(layersurface->layerSurface->_namespace ? layersurface->layerSurface->_namespace : "")}); EMIT_HOOK_EVENT("closeLayer", layersurface); + std::erase(g_pInputManager->m_dExclusiveLSes, layersurface); + + if (!g_pInputManager->m_dExclusiveLSes.empty()) + g_pCompositor->focusSurface(g_pInputManager->m_dExclusiveLSes[0]->layerSurface->surface); + if (!g_pCompositor->getMonitorFromID(layersurface->monitorID) || g_pCompositor->m_bUnsafeState) { Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring."); diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp index 6b77d9c5..f2ff726b 100644 --- a/src/events/Misc.cpp +++ b/src/events/Misc.cpp @@ -175,11 +175,17 @@ void Events::listener_sessionActive(wl_listener* listener, void* data) { void Events::listener_powerMgrSetMode(wl_listener* listener, void* data) { Debug::log(LOG, "PowerMgr set mode!"); - const auto EVENT = (wlr_output_power_v1_set_mode_event*)data; + const auto EVENT = (wlr_output_power_v1_set_mode_event*)data; + const auto PMONITOR = g_pCompositor->getMonitorFromOutput(EVENT->output); - wlr_output_enable(EVENT->output, EVENT->mode == 1); + if (!PMONITOR) { + Debug::log(ERR, "Invalid powerMgrSetMode output"); + return; + } - if (!wlr_output_commit(EVENT->output)) + wlr_output_state_set_enabled(PMONITOR->state.wlr(), EVENT->mode == 1); + + if (!PMONITOR->state.commit()) Debug::log(ERR, "Couldn't set power mode"); } @@ -227,14 +233,7 @@ void Events::listener_setCursorShape(wl_listener* listener, void* data) { void Events::listener_newTearingHint(wl_listener* listener, void* data) { const auto TCTL = (wlr_tearing_control_v1*)data; - const auto PWINDOW = g_pCompositor->getWindowFromSurface(TCTL->surface); - - if (!PWINDOW) { - Debug::log(ERR, "Tearing hint {} was attached to an unknown surface", (uintptr_t)data); - return; - } - - Debug::log(LOG, "New tearing hint for window {} at {}", PWINDOW, (uintptr_t)data); + Debug::log(LOG, "New tearing hint at {:x}", (uintptr_t)data); const auto NEWCTRL = g_pHyprRenderer->m_vTearingControllers.emplace_back(std::make_unique()).get(); NEWCTRL->pWlrHint = (wlr_tearing_control_v1*)data; @@ -242,7 +241,7 @@ void Events::listener_newTearingHint(wl_listener* listener, void* data) { NEWCTRL->hyprListener_destroy.initCallback( &NEWCTRL->pWlrHint->events.destroy, [&](void* owner, void* data) { - Debug::log(LOG, "Destroyed {} tearing hint", (uintptr_t)((STearingController*)owner)->pWlrHint); + Debug::log(LOG, "Destroyed {:x} tearing hint", (uintptr_t)((STearingController*)owner)->pWlrHint); std::erase_if(g_pHyprRenderer->m_vTearingControllers, [&](const auto& other) { return other.get() == owner; }); }, @@ -258,7 +257,7 @@ void Events::listener_newTearingHint(wl_listener* listener, void* data) { if (PWINDOW) { PWINDOW->m_bTearingHint = (bool)TEARINGHINT->pWlrHint->current; - Debug::log(LOG, "Hint {} (window {}) set tearing hint to {}", (uintptr_t)TEARINGHINT->pWlrHint, PWINDOW, (uint32_t)TEARINGHINT->pWlrHint->current); + Debug::log(LOG, "Hint {:x} (window {}) set tearing hint to {}", (uintptr_t)TEARINGHINT->pWlrHint, PWINDOW, (uint32_t)TEARINGHINT->pWlrHint->current); } }, NEWCTRL, "TearingController"); diff --git a/src/events/Monitors.cpp b/src/events/Monitors.cpp index 13f3f829..ddbc81f7 100644 --- a/src/events/Monitors.cpp +++ b/src/events/Monitors.cpp @@ -109,7 +109,7 @@ void Events::listener_newOutput(wl_listener* listener, void* data) { for (auto& w : g_pCompositor->m_vWindows) { if (w->m_iMonitorID == PNEWMONITOR->ID) { w->m_iLastSurfaceMonitorID = -1; - w->updateSurfaceOutputs(); + w->updateSurfaceScaleTransformDetails(); } } } @@ -209,10 +209,10 @@ void Events::listener_monitorDestroy(void* owner, void* data) { } void Events::listener_monitorStateRequest(void* owner, void* data) { - const auto PMONITOR = (CMonitor*)owner; - const auto E = (wlr_output_event_request_state*)data; + //const auto PMONITOR = (CMonitor*)owner; + //const auto E = (wlr_output_event_request_state*)data; - wlr_output_commit_state(PMONITOR->output, E->state); + // TODO: maybe don't ignore? } void Events::listener_monitorDamage(void* owner, void* data) { diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index ce5103ee..cf5d0ab5 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -639,7 +639,8 @@ void Events::listener_mapWindow(void* owner, void* data) { g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->scale); g_pCompositor->setPreferredTransformForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->transform); - g_pInputManager->sendMotionEventsToFocused(); + if (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->constraintActive) + g_pInputManager->sendMotionEventsToFocused(); // fix some xwayland apps that don't behave nicely PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize; @@ -803,8 +804,6 @@ void Events::listener_commitWindow(void* owner, void* data) { PWINDOW->m_pPendingSizeAck.reset(); } - PWINDOW->updateSurfaceOutputs(); - g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface.wlr(), PWINDOW->m_vRealPosition.goalv().x, PWINDOW->m_vRealPosition.goalv().y, PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0); @@ -1054,11 +1053,8 @@ void Events::listener_configureX11(void* owner, void* data) { static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue; if (*PXWLFORCESCALEZERO) { - if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) { - const Vector2D DELTA = PWINDOW->m_vRealSize.goalv() - PWINDOW->m_vRealSize.goalv() / PMONITOR->scale; + if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goalv() / PMONITOR->scale); - PWINDOW->m_vRealPosition.setValueAndWarp(PWINDOW->m_vRealPosition.goalv() + DELTA / 2.0); - } } PWINDOW->m_vPosition = PWINDOW->m_vRealPosition.vec(); @@ -1139,8 +1135,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) { g_pHyprRenderer->damageWindow(PWINDOW); PWINDOW->m_vReportedPosition = PWINDOW->m_vRealPosition.goalv(); - PWINDOW->m_vReportedSize = PWINDOW->m_vRealSize.goalv(); - PWINDOW->m_vPendingReportedSize = PWINDOW->m_vReportedSize; + PWINDOW->m_vPendingReportedSize = PWINDOW->m_vRealSize.goalv(); } } diff --git a/src/helpers/Box.cpp b/src/helpers/Box.cpp index 3d57bc79..d3f5d36c 100644 --- a/src/helpers/Box.cpp +++ b/src/helpers/Box.cpp @@ -105,6 +105,13 @@ CBox& CBox::expand(const double& value) { return *this; } +CBox& CBox::noNegativeSize() { + std::clamp(w, 0.0, std::numeric_limits::infinity()); + std::clamp(h, 0.0, std::numeric_limits::infinity()); + + return *this; +} + CBox CBox::roundInternal() { float newW = x + w - std::floor(x); float newH = y + h - std::floor(y); diff --git a/src/helpers/Box.hpp b/src/helpers/Box.hpp index a1ed83be..fd31a7b7 100644 --- a/src/helpers/Box.hpp +++ b/src/helpers/Box.hpp @@ -51,6 +51,7 @@ class CBox { CBox& transform(const wl_output_transform t, double w, double h); CBox& addExtents(const SWindowDecorationExtents& e); CBox& expand(const double& value); + CBox& noNegativeSize(); CBox copy() const; diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp index 812b6bc6..42d6bd47 100644 --- a/src/helpers/MiscFunctions.cpp +++ b/src/helpers/MiscFunctions.cpp @@ -159,6 +159,13 @@ void addWLSignal(wl_signal* pSignal, wl_listener* pListener, void* pOwner, const Debug::log(LOG, "Registered signal for owner {:x}: {:x} -> {:x} (owner: {})", (uintptr_t)pOwner, (uintptr_t)pSignal, (uintptr_t)pListener, ownerString); } +void removeWLSignal(wl_listener* pListener) { + wl_list_remove(&pListener->link); + wl_list_init(&pListener->link); + + Debug::log(LOG, "Removed listener {:x}", (uintptr_t)pListener); +} + void handleNoop(struct wl_listener* listener, void* data) { // Do nothing } diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp index 08d3ac26..67f37c8a 100644 --- a/src/helpers/MiscFunctions.hpp +++ b/src/helpers/MiscFunctions.hpp @@ -16,6 +16,7 @@ struct SCallstackFrameInfo { std::string absolutePath(const std::string&, const std::string&); void addWLSignal(wl_signal*, wl_listener*, void* pOwner, const std::string& ownerString); +void removeWLSignal(wl_listener*); std::string escapeJSONStrings(const std::string& str); std::string removeBeginEndSpacesTabs(std::string); bool isNumber(const std::string&, bool allowfloat = false); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 3b4f4016..f9cc5472 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -8,7 +8,7 @@ int ratHandler(void* data) { return 1; } -CMonitor::CMonitor() { +CMonitor::CMonitor() : state(this) { wlr_damage_ring_init(&damage); } @@ -43,8 +43,8 @@ void CMonitor::onConnect(bool noRule) { tearingState.canTear = wlr_backend_is_drm(output->backend); // tearing only works on drm if (m_bEnabled) { - wlr_output_enable(output, 1); - wlr_output_commit(output); + wlr_output_state_set_enabled(state.wlr(), true); + state.commit(); return; } @@ -63,8 +63,8 @@ void CMonitor::onConnect(bool noRule) { // if it's disabled, disable and ignore if (monitorRule.disabled) { - wlr_output_set_scale(output, 1); - wlr_output_set_transform(output, WL_OUTPUT_TRANSFORM_NORMAL); + wlr_output_state_set_scale(state.wlr(), 1); + wlr_output_state_set_transform(state.wlr(), WL_OUTPUT_TRANSFORM_NORMAL); auto PREFSTATE = wlr_output_preferred_mode(output); @@ -72,9 +72,9 @@ void CMonitor::onConnect(bool noRule) { wlr_output_mode* mode; wl_list_for_each(mode, &output->modes, link) { - wlr_output_set_mode(output, PREFSTATE); + wlr_output_state_set_mode(state.wlr(), mode); - if (!wlr_output_test(output)) + if (!wlr_output_test_state(output, state.wlr())) continue; PREFSTATE = mode; @@ -83,13 +83,13 @@ void CMonitor::onConnect(bool noRule) { } if (PREFSTATE) - wlr_output_set_mode(output, PREFSTATE); + wlr_output_state_set_mode(state.wlr(), PREFSTATE); else Debug::log(WARN, "No mode found for disabled output {}", output->name); - wlr_output_enable(output, 0); + wlr_output_state_set_enabled(state.wlr(), 0); - if (!wlr_output_commit(output)) + if (!state.commit()) Debug::log(ERR, "Couldn't commit disabled state on output {}", output->name); m_bEnabled = false; @@ -130,13 +130,14 @@ void CMonitor::onConnect(bool noRule) { m_bEnabled = true; - wlr_output_enable(output, 1); + wlr_output_state_set_enabled(state.wlr(), 1); // set mode, also applies if (!noRule) g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true); - wlr_output_commit(output); + if (!state.commit()) + Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onCommit"); wlr_damage_ring_set_bounds(&damage, vecTransformedSize.x, vecTransformedSize.y); @@ -283,9 +284,10 @@ void CMonitor::onDisconnect(bool destroy) { if (!destroy) wlr_output_layout_remove(g_pCompositor->m_sWLROutputLayout, output); - wlr_output_enable(output, false); + wlr_output_state_set_enabled(state.wlr(), false); - wlr_output_commit(output); + if (!state.commit()) + Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onDisconnect"); if (g_pCompositor->m_pLastMonitor == this) g_pCompositor->setActiveMonitor(BACKUPMON); @@ -502,7 +504,7 @@ float CMonitor::getDefaultScale() { return 1; } -void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool noMouseMove) { +void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool noMouseMove, bool noFocus) { if (!pWorkspace) return; @@ -533,7 +535,7 @@ void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool } } - if (!g_pCompositor->m_pLastMonitor->specialWorkspaceID) { + if (!noFocus && !g_pCompositor->m_pLastMonitor->specialWorkspaceID) { static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; CWindow* pWindow = pWorkspace->getLastFocusedWindow(); @@ -621,7 +623,7 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) { for (auto& w : g_pCompositor->m_vWindows) { if (w->m_iWorkspaceID == pWorkspace->m_iID) { w->m_iMonitorID = ID; - w->updateSurfaceOutputs(); + w->updateSurfaceScaleTransformDetails(); const auto MIDDLE = w->middle(); if (w->m_bIsFloating && !VECINRECT(MIDDLE, vecPosition.x, vecPosition.y, vecPosition.x + vecSize.x, vecPosition.y + vecSize.y) && w->m_iX11Type != 2) { @@ -678,3 +680,32 @@ void CMonitor::updateMatrix() { wlr_matrix_translate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0); } } + +CMonitorState::CMonitorState(CMonitor* owner) { + m_pOwner = owner; + wlr_output_state_init(&m_state); +} + +CMonitorState::~CMonitorState() { + wlr_output_state_finish(&m_state); +} + +wlr_output_state* CMonitorState::wlr() { + return &m_state; +} + +void CMonitorState::clear() { + wlr_output_state_finish(&m_state); + m_state = {0}; + wlr_output_state_init(&m_state); +} + +bool CMonitorState::commit() { + bool ret = wlr_output_commit_state(m_pOwner->output, &m_state); + clear(); + return ret; +} + +bool CMonitorState::test() { + return wlr_output_test_state(m_pOwner->output, &m_state); +} diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 2b196c96..43de8d12 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -25,6 +25,25 @@ struct SMonitorRule { std::optional vrr; }; +class CMonitor; + +// Class for wrapping the wlr state +class CMonitorState { + public: + CMonitorState(CMonitor* owner); + ~CMonitorState(); + + wlr_output_state* wlr(); + void clear(); + // commit() will also clear() + bool commit(); + bool test(); + + private: + wlr_output_state m_state = {0}; + CMonitor* m_pOwner; +}; + class CMonitor { public: CMonitor(); @@ -51,6 +70,8 @@ class CMonitor { drmModeModeInfo customDrmMode = {}; + CMonitorState state; + // WLR stuff wlr_damage_ring damage; wlr_output* output = nullptr; @@ -120,7 +141,7 @@ class CMonitor { void setMirror(const std::string&); bool isMirror(); float getDefaultScale(); - void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false); + void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false); void changeWorkspace(const int& id, bool internal = false); void setSpecialWorkspace(CWorkspace* const pWorkspace); void setSpecialWorkspace(const int& id); diff --git a/src/helpers/Splashes.hpp b/src/helpers/Splashes.hpp index f5990506..e7a1cb5f 100644 --- a/src/helpers/Splashes.hpp +++ b/src/helpers/Splashes.hpp @@ -19,6 +19,21 @@ inline const std::vector SPLASHES = { "Compile, wait for 20 minutes, notice a new commit, compile again.", "To rice, or not to rice, that is the question.", "Now available on Fedora!", + "\"Hyprland is so good it starts with a capital letter\" - Hazel", + "\"please make this message a splash\" - eriedaberrie", + "\"the only wayland compositor powered by fried chicken\" - raf", + "\"This will never get into Hyprland\" - Flafy", + "\"Hyprland only gives you up on -git\" - fazzi", + "Segmentation fault (core dumped)", + "\"disabling hyprland logo is a war crime\" - vaxry", + "some basic startup code", + "\"I think I am addicted to hyprland\" - mathisbuilder", + "\"hyprland is the most important package in the arch repos\" - jacekpoz", + "Thanks Brodie!", + "Thanks fufexan!", + "Thanks raf!", + "You can't use --splash to change this message :)", + "Hyprland will overtake Gnome in popularity by [insert year]", // music reference / quote section "J'remue le ciel, le jour, la nuit.", "aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi!", diff --git a/src/helpers/SubsurfaceTree.cpp b/src/helpers/SubsurfaceTree.cpp index 5891411b..a8226e19 100644 --- a/src/helpers/SubsurfaceTree.cpp +++ b/src/helpers/SubsurfaceTree.cpp @@ -178,6 +178,9 @@ void Events::listener_mapSubsurface(void* owner, void* data) { Debug::log(LOG, "Subsurface {:x} mapped", (uintptr_t)subsurface->pSubsurface); subsurface->pChild = createSubsurfaceNode(subsurface->pParent, subsurface, subsurface->pSubsurface->surface, subsurface->pWindowOwner); + + if (subsurface->pWindowOwner) + subsurface->pWindowOwner->updateSurfaceScaleTransformDetails(); } void Events::listener_unmapSubsurface(void* owner, void* data) { @@ -259,13 +262,11 @@ void Events::listener_commitSubsurface(void* owner, void* data) { // tearing: if solitary, redraw it. This still might be a single surface window const auto PMONITOR = g_pCompositor->getMonitorFromID(pNode->pWindowOwner->m_iMonitorID); - if (PMONITOR->solitaryClient == pNode->pWindowOwner && pNode->pWindowOwner->canBeTorn() && PMONITOR->tearingState.canTear && + if (PMONITOR && PMONITOR->solitaryClient == pNode->pWindowOwner && pNode->pWindowOwner->canBeTorn() && PMONITOR->tearingState.canTear && pNode->pSurface->wlr()->current.committed & WLR_SURFACE_STATE_BUFFER) { - CRegion damageBox; - wlr_surface_get_effective_damage(pNode->pSurface->wlr(), damageBox.pixman()); + CRegion damageBox{&pNode->pSurface->wlr()->buffer_damage}; if (!damageBox.empty()) { - if (PMONITOR->tearingState.busy) { PMONITOR->tearingState.frameScheduledWhileBusy = true; } else { diff --git a/src/helpers/Vector2D.cpp b/src/helpers/Vector2D.cpp index 7bc6412d..fb432936 100644 --- a/src/helpers/Vector2D.cpp +++ b/src/helpers/Vector2D.cpp @@ -42,14 +42,6 @@ double Vector2D::distance(const Vector2D& other) const { return std::sqrt(dx * dx + dy * dy); } -bool Vector2D::inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const { - const auto a = ((p2.y - p3.y) * (x - p3.x) + (p3.x - p2.x) * (y - p3.y)) / ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y)); - const auto b = ((p3.y - p1.y) * (x - p3.x) + (p1.x - p3.x) * (y - p3.y)) / ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y)); - const auto c = 1 - a - b; - - return 0 <= a && a <= 1 && 0 <= b && b <= 1 && 0 <= c && c <= 1; -} - double Vector2D::size() const { return std::sqrt(x * x + y * y); } diff --git a/src/helpers/Vector2D.hpp b/src/helpers/Vector2D.hpp index 34b06862..16f18793 100644 --- a/src/helpers/Vector2D.hpp +++ b/src/helpers/Vector2D.hpp @@ -92,8 +92,6 @@ class Vector2D { Vector2D floor() const; Vector2D round() const; - - bool inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const; }; /** diff --git a/src/helpers/WLListener.cpp b/src/helpers/WLListener.cpp index 402c999c..f499b929 100644 --- a/src/helpers/WLListener.cpp +++ b/src/helpers/WLListener.cpp @@ -7,13 +7,15 @@ void handleWrapped(wl_listener* listener, void* data) { CHyprWLListener::SWrapper* pWrap = wl_container_of(listener, pWrap, m_sListener); - g_pWatchdog->startWatching(); + if (g_pWatchdog) + g_pWatchdog->startWatching(); try { pWrap->m_pSelf->emit(data); } catch (std::exception& e) { Debug::log(ERR, "Listener {} timed out and was killed by Watchdog!!!", (uintptr_t)listener); } - g_pWatchdog->endWatching(); + if (g_pWatchdog) + g_pWatchdog->endWatching(); } CHyprWLListener::CHyprWLListener(wl_signal* pSignal, std::function callback, void* pOwner) { diff --git a/src/helpers/WLSurface.hpp b/src/helpers/WLSurface.hpp index faef4353..03714bbf 100644 --- a/src/helpers/WLSurface.hpp +++ b/src/helpers/WLSurface.hpp @@ -28,8 +28,14 @@ class CWLSurface { bool m_bFillIgnoreSmall = false; // if present, means this is a base surface of a window. Cleaned on unassign() - CWindow* m_pOwner = nullptr; + CWindow* m_pOwner = nullptr; + // track surface data and avoid dupes + float m_fLastScale = 0; + int m_iLastScale = 0; + wl_output_transform m_eLastTransform = (wl_output_transform)-1; + + // CWLSurface& operator=(wlr_surface* pSurface) { destroy(); m_pWLRSurface = pSurface; diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp index 76b9aeb4..4058121f 100644 --- a/src/layout/DwindleLayout.cpp +++ b/src/layout/DwindleLayout.cpp @@ -22,13 +22,13 @@ void SDwindleNodeData::recalcSizePosRecursive(bool force, bool horizontalOverrid if (SPLITSIDE) { // split left/right const float FIRSTSIZE = box.w / 2.0 * splitRatio; - children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h}; - children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h}; + children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h}.noNegativeSize(); + children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h}.noNegativeSize(); } else { // split top/bottom const float FIRSTSIZE = box.h / 2.0 * splitRatio; - children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE}; - children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE}; + children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE}.noNegativeSize(); + children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE}.noNegativeSize(); } children[0]->recalcSizePosRecursive(force); @@ -64,6 +64,21 @@ SDwindleNodeData* CHyprDwindleLayout::getFirstNodeOnWorkspace(const int& id) { return nullptr; } +SDwindleNodeData* CHyprDwindleLayout::getClosestNodeOnWorkspace(const int& id, const Vector2D& point) { + SDwindleNodeData* res = nullptr; + double distClosest = -1; + for (auto& n : m_lDwindleNodesData) { + if (n.workspaceID == id && n.pWindow && g_pCompositor->windowValidMapped(n.pWindow)) { + auto distAnother = vecToRectDistanceSquared(point, n.box.pos(), n.box.pos() + n.box.size()); + if (!res || distAnother < distClosest) { + res = &n; + distClosest = distAnother; + } + } + } + return res; +} + SDwindleNodeData* CHyprDwindleLayout::getNodeFromWindow(CWindow* pWindow) { for (auto& n : m_lDwindleNodesData) { if (n.pWindow == pWindow && !n.isNode) @@ -254,26 +269,25 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire const auto MOUSECOORDS = m_vOverrideFocalPoint.value_or(g_pInputManager->getMouseCoordsInternal()); const auto MONFROMCURSOR = g_pCompositor->getMonitorFromVector(MOUSECOORDS); - const auto TARGETCOORDS = PMONITOR->ID == MONFROMCURSOR->ID && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR) ? pWindow->middle() : MOUSECOORDS; if (PMONITOR->ID == MONFROMCURSOR->ID && (PNODE->workspaceID == PMONITOR->activeWorkspace || (g_pCompositor->isWorkspaceSpecial(PNODE->workspaceID) && PMONITOR->specialWorkspaceID)) && !*PUSEACTIVE) { - OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowTiled(TARGETCOORDS)); + OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowTiled(MOUSECOORDS)); - // happens on reserved area - if (!OPENINGON && g_pCompositor->getWindowsOnWorkspace(PNODE->workspaceID) > 0) - OPENINGON = getFirstNodeOnWorkspace(PMONITOR->activeWorkspace); + if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR)) + OPENINGON = getClosestNodeOnWorkspace(PNODE->workspaceID, MOUSECOORDS); } else if (*PUSEACTIVE) { if (g_pCompositor->m_pLastWindow && !g_pCompositor->m_pLastWindow->m_bIsFloating && g_pCompositor->m_pLastWindow != pWindow && g_pCompositor->m_pLastWindow->m_iWorkspaceID == pWindow->m_iWorkspaceID && g_pCompositor->m_pLastWindow->m_bIsMapped) { OPENINGON = getNodeFromWindow(g_pCompositor->m_pLastWindow); } else { - OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowTiled(TARGETCOORDS)); + OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowTiled(MOUSECOORDS)); } - if (!OPENINGON && g_pCompositor->getWindowsOnWorkspace(PNODE->workspaceID) > 0) - OPENINGON = getFirstNodeOnWorkspace(PMONITOR->activeWorkspace); + if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR)) + OPENINGON = getClosestNodeOnWorkspace(PNODE->workspaceID, MOUSECOORDS); + } else OPENINGON = getFirstNodeOnWorkspace(pWindow->m_iWorkspaceID); @@ -386,34 +400,41 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire if (*PERMANENTDIRECTIONOVERRIDE == 0) overrideDirection = DIRECTION_DEFAULT; } else if (*PSMARTSPLIT == 1) { - const auto tl = NEWPARENT->box.pos(); - const auto tr = NEWPARENT->box.pos() + Vector2D(NEWPARENT->box.w, 0); - const auto bl = NEWPARENT->box.pos() + Vector2D(0, NEWPARENT->box.h); - const auto br = NEWPARENT->box.pos() + NEWPARENT->box.size(); - const auto cc = NEWPARENT->box.pos() + NEWPARENT->box.size() / 2; + const auto PARENT_CENTER = NEWPARENT->box.pos() + NEWPARENT->box.size() / 2; + const auto PARENT_PROPORTIONS = NEWPARENT->box.h / NEWPARENT->box.w; + const auto DELTA = MOUSECOORDS - PARENT_CENTER; + const auto DELTA_SLOPE = DELTA.y / DELTA.x; - if (TARGETCOORDS.inTriangle(tl, tr, cc)) { - NEWPARENT->splitTop = true; - NEWPARENT->children[0] = PNODE; - NEWPARENT->children[1] = OPENINGON; - } else if (TARGETCOORDS.inTriangle(tr, cc, br)) { - NEWPARENT->splitTop = false; - NEWPARENT->children[0] = OPENINGON; - NEWPARENT->children[1] = PNODE; - } else if (TARGETCOORDS.inTriangle(br, bl, cc)) { - NEWPARENT->splitTop = true; - NEWPARENT->children[0] = OPENINGON; - NEWPARENT->children[1] = PNODE; + if (abs(DELTA_SLOPE) < PARENT_PROPORTIONS) { + if (DELTA.x > 0) { + // right + NEWPARENT->splitTop = false; + NEWPARENT->children[0] = OPENINGON; + NEWPARENT->children[1] = PNODE; + } else { + // left + NEWPARENT->splitTop = false; + NEWPARENT->children[0] = PNODE; + NEWPARENT->children[1] = OPENINGON; + } } else { - NEWPARENT->splitTop = false; - NEWPARENT->children[0] = PNODE; - NEWPARENT->children[1] = OPENINGON; + if (DELTA.y > 0) { + // bottom + NEWPARENT->splitTop = true; + NEWPARENT->children[0] = OPENINGON; + NEWPARENT->children[1] = PNODE; + } else { + // top + NEWPARENT->splitTop = true; + NEWPARENT->children[0] = PNODE; + NEWPARENT->children[1] = OPENINGON; + } } } else if (*PFORCESPLIT == 0 || !pWindow->m_bFirstMap) { if ((SIDEBYSIDE && - VECINRECT(TARGETCOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y + NEWPARENT->box.h)) || + VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y + NEWPARENT->box.h)) || (!SIDEBYSIDE && - VECINRECT(TARGETCOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w, NEWPARENT->box.y + NEWPARENT->box.h / 2.f))) { + VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w, NEWPARENT->box.y + NEWPARENT->box.h / 2.f))) { // we are hovering over the first node, make PNODE first. NEWPARENT->children[1] = OPENINGON; NEWPARENT->children[0] = PNODE; @@ -776,6 +797,14 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscree return; } + // save position and size if floating + if (pWindow->m_bIsFloating && on) { + pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv(); + pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv(); + pWindow->m_vPosition = pWindow->m_vRealPosition.goalv(); + pWindow->m_vSize = pWindow->m_vRealSize.goalv(); + } + // otherwise, accept it. pWindow->m_bIsFullscreen = on; PWORKSPACE->m_bHasFullscreenWindow = !PWORKSPACE->m_bHasFullscreenWindow; @@ -803,14 +832,6 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscree PWORKSPACE->m_efFullscreenMode = fullscreenMode; - // save position and size if floating - if (pWindow->m_bIsFloating) { - pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv(); - pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv(); - pWindow->m_vPosition = pWindow->m_vRealPosition.goalv(); - pWindow->m_vSize = pWindow->m_vRealSize.goalv(); - } - // apply new pos and size being monitors' box if (fullscreenMode == FULLSCREEN_FULL) { pWindow->m_vRealPosition = PMONITOR->vecPosition; diff --git a/src/layout/DwindleLayout.hpp b/src/layout/DwindleLayout.hpp index c2d47d88..780dd121 100644 --- a/src/layout/DwindleLayout.hpp +++ b/src/layout/DwindleLayout.hpp @@ -79,6 +79,7 @@ class CHyprDwindleLayout : public IHyprLayout { void applyNodeDataToWindow(SDwindleNodeData*, bool force = false); SDwindleNodeData* getNodeFromWindow(CWindow*); SDwindleNodeData* getFirstNodeOnWorkspace(const int&); + SDwindleNodeData* getClosestNodeOnWorkspace(const int&, const Vector2D&); SDwindleNodeData* getMasterNodeOnWorkspace(const int&); void toggleSplit(CWindow*); diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp index 6a03ac80..705f48cf 100644 --- a/src/layout/IHyprLayout.cpp +++ b/src/layout/IHyprLayout.cpp @@ -314,8 +314,7 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) { if ((abs(TICKDELTA.x) < 1.f && abs(TICKDELTA.y) < 1.f) || (std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - TIMER).count() < - 1000.0 / g_pHyprRenderer->m_pMostHzMonitor->refreshRate && - (*PANIMATEMOUSE || *PANIMATE))) + 1000.0 / g_pHyprRenderer->m_pMostHzMonitor->refreshRate)) return; TIMER = std::chrono::high_resolution_clock::now(); @@ -469,16 +468,18 @@ void IHyprLayout::changeWindowFloatingMode(CWindow* pWindow) { g_pCompositor->changeWindowZOrder(pWindow, true); + CBox wb = {pWindow->m_vRealPosition.goalv() + (pWindow->m_vRealSize.goalv() - pWindow->m_vLastFloatingSize) / 2.f, pWindow->m_vLastFloatingSize}; + wb.round(); + if (DELTALESSTHAN(pWindow->m_vRealSize.vec().x, pWindow->m_vLastFloatingSize.x, 10) && DELTALESSTHAN(pWindow->m_vRealSize.vec().y, pWindow->m_vLastFloatingSize.y, 10)) { - pWindow->m_vRealPosition = pWindow->m_vRealPosition.goalv() + (pWindow->m_vRealSize.goalv() - pWindow->m_vLastFloatingSize) / 2.f + Vector2D{10, 10}; - pWindow->m_vRealSize = pWindow->m_vLastFloatingSize - Vector2D{20, 20}; + wb = {wb.pos() + Vector2D{10, 10}, wb.size() - Vector2D{20, 20}}; } - pWindow->m_vRealPosition = pWindow->m_vRealPosition.goalv() + (pWindow->m_vRealSize.goalv() - pWindow->m_vLastFloatingSize) / 2.f; - pWindow->m_vRealSize = pWindow->m_vLastFloatingSize; + pWindow->m_vRealPosition = wb.pos(); + pWindow->m_vRealSize = wb.size(); - pWindow->m_vSize = pWindow->m_vRealSize.goalv(); - pWindow->m_vPosition = pWindow->m_vRealPosition.goalv(); + pWindow->m_vSize = wb.pos(); + pWindow->m_vPosition = wb.size(); g_pHyprRenderer->damageMonitor(g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID)); diff --git a/src/layout/MasterLayout.cpp b/src/layout/MasterLayout.cpp index 9a76801c..c27e7e9e 100644 --- a/src/layout/MasterLayout.cpp +++ b/src/layout/MasterLayout.cpp @@ -879,6 +879,14 @@ void CHyprMasterLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreen return; } + // save position and size if floating + if (pWindow->m_bIsFloating && on) { + pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv(); + pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv(); + pWindow->m_vPosition = pWindow->m_vRealPosition.goalv(); + pWindow->m_vSize = pWindow->m_vRealSize.goalv(); + } + // otherwise, accept it. pWindow->m_bIsFullscreen = on; PWORKSPACE->m_bHasFullscreenWindow = !PWORKSPACE->m_bHasFullscreenWindow; @@ -906,14 +914,6 @@ void CHyprMasterLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreen PWORKSPACE->m_efFullscreenMode = fullscreenMode; - // save position and size if floating - if (pWindow->m_bIsFloating) { - pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv(); - pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv(); - pWindow->m_vPosition = pWindow->m_vRealPosition.goalv(); - pWindow->m_vSize = pWindow->m_vRealSize.goalv(); - } - // apply new pos and size being monitors' box if (fullscreenMode == FULLSCREEN_FULL) { pWindow->m_vRealPosition = PMONITOR->vecPosition; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 21cc1f74..eae86d39 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -19,64 +19,65 @@ CKeybindManager::CKeybindManager() { // initialize all dispatchers - m_mDispatchers["exec"] = spawn; - m_mDispatchers["execr"] = spawnRaw; - m_mDispatchers["killactive"] = killActive; - m_mDispatchers["closewindow"] = kill; - m_mDispatchers["togglefloating"] = toggleActiveFloating; - m_mDispatchers["workspace"] = changeworkspace; - m_mDispatchers["renameworkspace"] = renameWorkspace; - m_mDispatchers["fullscreen"] = fullscreenActive; - m_mDispatchers["fakefullscreen"] = fakeFullscreenActive; - m_mDispatchers["movetoworkspace"] = moveActiveToWorkspace; - m_mDispatchers["movetoworkspacesilent"] = moveActiveToWorkspaceSilent; - m_mDispatchers["pseudo"] = toggleActivePseudo; - m_mDispatchers["movefocus"] = moveFocusTo; - m_mDispatchers["movewindow"] = moveActiveTo; - m_mDispatchers["swapwindow"] = swapActive; - m_mDispatchers["centerwindow"] = centerWindow; - m_mDispatchers["togglegroup"] = toggleGroup; - m_mDispatchers["changegroupactive"] = changeGroupActive; - m_mDispatchers["movegroupwindow"] = moveGroupWindow; - m_mDispatchers["togglesplit"] = toggleSplit; - m_mDispatchers["splitratio"] = alterSplitRatio; - m_mDispatchers["focusmonitor"] = focusMonitor; - m_mDispatchers["movecursortocorner"] = moveCursorToCorner; - m_mDispatchers["movecursor"] = moveCursor; - m_mDispatchers["workspaceopt"] = workspaceOpt; - m_mDispatchers["exit"] = exitHyprland; - m_mDispatchers["movecurrentworkspacetomonitor"] = moveCurrentWorkspaceToMonitor; - m_mDispatchers["moveworkspacetomonitor"] = moveWorkspaceToMonitor; - m_mDispatchers["togglespecialworkspace"] = toggleSpecialWorkspace; - m_mDispatchers["forcerendererreload"] = forceRendererReload; - m_mDispatchers["resizeactive"] = resizeActive; - m_mDispatchers["moveactive"] = moveActive; - m_mDispatchers["cyclenext"] = circleNext; - m_mDispatchers["focuswindowbyclass"] = focusWindow; - m_mDispatchers["focuswindow"] = focusWindow; - m_mDispatchers["submap"] = setSubmap; - m_mDispatchers["pass"] = pass; - m_mDispatchers["layoutmsg"] = layoutmsg; - m_mDispatchers["toggleopaque"] = toggleOpaque; - m_mDispatchers["dpms"] = dpms; - m_mDispatchers["movewindowpixel"] = moveWindow; - m_mDispatchers["resizewindowpixel"] = resizeWindow; - m_mDispatchers["swapnext"] = swapnext; - m_mDispatchers["swapactiveworkspaces"] = swapActiveWorkspaces; - m_mDispatchers["pin"] = pinActive; - m_mDispatchers["mouse"] = mouse; - m_mDispatchers["bringactivetotop"] = bringActiveToTop; - m_mDispatchers["alterzorder"] = alterZOrder; - m_mDispatchers["focusurgentorlast"] = focusUrgentOrLast; - m_mDispatchers["focuscurrentorlast"] = focusCurrentOrLast; - m_mDispatchers["lockgroups"] = lockGroups; - m_mDispatchers["lockactivegroup"] = lockActiveGroup; - m_mDispatchers["moveintogroup"] = moveIntoGroup; - m_mDispatchers["moveoutofgroup"] = moveOutOfGroup; - m_mDispatchers["movewindoworgroup"] = moveWindowOrGroup; - m_mDispatchers["setignoregrouplock"] = setIgnoreGroupLock; - m_mDispatchers["denywindowfromgroup"] = denyWindowFromGroup; - m_mDispatchers["global"] = global; + m_mDispatchers["exec"] = spawn; + m_mDispatchers["execr"] = spawnRaw; + m_mDispatchers["killactive"] = killActive; + m_mDispatchers["closewindow"] = kill; + m_mDispatchers["togglefloating"] = toggleActiveFloating; + m_mDispatchers["workspace"] = changeworkspace; + m_mDispatchers["renameworkspace"] = renameWorkspace; + m_mDispatchers["fullscreen"] = fullscreenActive; + m_mDispatchers["fakefullscreen"] = fakeFullscreenActive; + m_mDispatchers["movetoworkspace"] = moveActiveToWorkspace; + m_mDispatchers["movetoworkspacesilent"] = moveActiveToWorkspaceSilent; + m_mDispatchers["pseudo"] = toggleActivePseudo; + m_mDispatchers["movefocus"] = moveFocusTo; + m_mDispatchers["movewindow"] = moveActiveTo; + m_mDispatchers["swapwindow"] = swapActive; + m_mDispatchers["centerwindow"] = centerWindow; + m_mDispatchers["togglegroup"] = toggleGroup; + m_mDispatchers["changegroupactive"] = changeGroupActive; + m_mDispatchers["movegroupwindow"] = moveGroupWindow; + m_mDispatchers["togglesplit"] = toggleSplit; + m_mDispatchers["splitratio"] = alterSplitRatio; + m_mDispatchers["focusmonitor"] = focusMonitor; + m_mDispatchers["movecursortocorner"] = moveCursorToCorner; + m_mDispatchers["movecursor"] = moveCursor; + m_mDispatchers["workspaceopt"] = workspaceOpt; + m_mDispatchers["exit"] = exitHyprland; + m_mDispatchers["movecurrentworkspacetomonitor"] = moveCurrentWorkspaceToMonitor; + m_mDispatchers["focusworkspaceoncurrentmonitor"] = focusWorkspaceOnCurrentMonitor; + m_mDispatchers["moveworkspacetomonitor"] = moveWorkspaceToMonitor; + m_mDispatchers["togglespecialworkspace"] = toggleSpecialWorkspace; + m_mDispatchers["forcerendererreload"] = forceRendererReload; + m_mDispatchers["resizeactive"] = resizeActive; + m_mDispatchers["moveactive"] = moveActive; + m_mDispatchers["cyclenext"] = circleNext; + m_mDispatchers["focuswindowbyclass"] = focusWindow; + m_mDispatchers["focuswindow"] = focusWindow; + m_mDispatchers["submap"] = setSubmap; + m_mDispatchers["pass"] = pass; + m_mDispatchers["layoutmsg"] = layoutmsg; + m_mDispatchers["toggleopaque"] = toggleOpaque; + m_mDispatchers["dpms"] = dpms; + m_mDispatchers["movewindowpixel"] = moveWindow; + m_mDispatchers["resizewindowpixel"] = resizeWindow; + m_mDispatchers["swapnext"] = swapnext; + m_mDispatchers["swapactiveworkspaces"] = swapActiveWorkspaces; + m_mDispatchers["pin"] = pinActive; + m_mDispatchers["mouse"] = mouse; + m_mDispatchers["bringactivetotop"] = bringActiveToTop; + m_mDispatchers["alterzorder"] = alterZOrder; + m_mDispatchers["focusurgentorlast"] = focusUrgentOrLast; + m_mDispatchers["focuscurrentorlast"] = focusCurrentOrLast; + m_mDispatchers["lockgroups"] = lockGroups; + m_mDispatchers["lockactivegroup"] = lockActiveGroup; + m_mDispatchers["moveintogroup"] = moveIntoGroup; + m_mDispatchers["moveoutofgroup"] = moveOutOfGroup; + m_mDispatchers["movewindoworgroup"] = moveWindowOrGroup; + m_mDispatchers["setignoregrouplock"] = setIgnoreGroupLock; + m_mDispatchers["denywindowfromgroup"] = denyWindowFromGroup; + m_mDispatchers["global"] = global; m_tScrollTimer.reset(); @@ -313,9 +314,10 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard .keysym = keysym, .keycode = KEYCODE, .modmaskAtPressTime = MODS, + .sent = true, }; - bool found = false; + bool suppressEvent = false; if (e->state == WL_KEYBOARD_KEY_STATE_PRESSED) { // clean repeat if (m_pActiveKeybindEventSource) { @@ -326,10 +328,12 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard m_dPressedKeys.push_back(KEY); - found = handleKeybinds(MODS, KEY, true); + suppressEvent = handleKeybinds(MODS, KEY, true); - if (found) + if (suppressEvent) shadowKeybinds(keysym, KEYCODE); + + m_dPressedKeys.back().sent = !suppressEvent; } else { // key release // clean repeat if (m_pActiveKeybindEventSource) { @@ -341,11 +345,10 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard bool foundInPressedKeys = false; for (auto it = m_dPressedKeys.begin(); it != m_dPressedKeys.end();) { if (it->keycode == KEYCODE) { - if (!foundInPressedKeys) { - found = handleKeybinds(MODS, *it, false); - foundInPressedKeys = true; - } - it = m_dPressedKeys.erase(it); + suppressEvent = handleKeybinds(MODS, *it, false); + foundInPressedKeys = true; + suppressEvent = !it->sent; + it = m_dPressedKeys.erase(it); } else { ++it; } @@ -353,13 +356,13 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard if (!foundInPressedKeys) { Debug::log(ERR, "BUG THIS: key not found in m_dPressedKeys"); // fallback with wrong `KEY.modmaskAtPressTime`, this can be buggy - found = handleKeybinds(MODS, KEY, false); + suppressEvent = handleKeybinds(MODS, KEY, false); } shadowKeybinds(); } - return !found && !mouseBindWasActive; + return !suppressEvent && !mouseBindWasActive; } bool CKeybindManager::onAxisEvent(wlr_pointer_axis_event* e) { @@ -396,7 +399,7 @@ bool CKeybindManager::onAxisEvent(wlr_pointer_axis_event* e) { bool CKeybindManager::onMouseEvent(wlr_pointer_button_event* e) { const auto MODS = g_pInputManager->accumulateModsFromAllKBs(); - bool found = false; + bool suppressEvent = false; m_uLastMouseCode = e->button; m_uLastCode = 0; @@ -414,19 +417,20 @@ bool CKeybindManager::onMouseEvent(wlr_pointer_button_event* e) { if (e->state == WLR_BUTTON_PRESSED) { m_dPressedKeys.push_back(KEY); - found = handleKeybinds(MODS, KEY, true); + suppressEvent = handleKeybinds(MODS, KEY, true); - if (found) + if (suppressEvent) shadowKeybinds(); + + m_dPressedKeys.back().sent = !suppressEvent; } else { bool foundInPressedKeys = false; for (auto it = m_dPressedKeys.begin(); it != m_dPressedKeys.end();) { if (it->keyName == KEY_NAME) { - if (!foundInPressedKeys) { - found = handleKeybinds(MODS, *it, false); - foundInPressedKeys = true; - } - it = m_dPressedKeys.erase(it); + suppressEvent = handleKeybinds(MODS, *it, false); + foundInPressedKeys = true; + suppressEvent = !it->sent; + it = m_dPressedKeys.erase(it); } else { ++it; } @@ -434,13 +438,13 @@ bool CKeybindManager::onMouseEvent(wlr_pointer_button_event* e) { if (!foundInPressedKeys) { Debug::log(ERR, "BUG THIS: key not found in m_dPressedKeys (2)"); // fallback with wrong `KEY.modmaskAtPressTime`, this can be buggy - found = handleKeybinds(MODS, KEY, false); + suppressEvent = handleKeybinds(MODS, KEY, false); } shadowKeybinds(); } - return !found && !mouseBindWasActive; + return !suppressEvent && !mouseBindWasActive; } void CKeybindManager::resizeWithBorder(wlr_pointer_button_event* e) { @@ -522,7 +526,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi if (!pressed) { // Require mods to be matching when the key was first pressed. - if (key.modmaskAtPressTime != modmask) { + if (key.modmaskAtPressTime != modmask && !k.ignoreMods) { // Handle properly `bindr` where a key is itself a bind mod for example: // "bindr = SUPER, SUPER_L, exec, $launcher". // This needs to be handled separately for the above case, because `key.modmaskAtPressTime` is set @@ -1449,7 +1453,7 @@ void CKeybindManager::renameWorkspace(std::string args) { } void CKeybindManager::exitHyprland(std::string argz) { - g_pCompositor->cleanup(); + g_pInputManager->m_bExitTriggered = true; } void CKeybindManager::moveCurrentWorkspaceToMonitor(std::string args) { @@ -1499,6 +1503,48 @@ void CKeybindManager::moveWorkspaceToMonitor(std::string args) { g_pCompositor->moveWorkspaceToMonitor(PWORKSPACE, PMONITOR); } +void CKeybindManager::focusWorkspaceOnCurrentMonitor(std::string args) { + std::string workspaceName; + const int WORKSPACEID = getWorkspaceIDFromString(args, workspaceName); + + if (WORKSPACEID == WORKSPACE_INVALID) { + Debug::log(ERR, "focusWorkspaceOnCurrentMonitor invalid workspace!"); + return; + } + + const auto PCURRMONITOR = g_pCompositor->getMonitorFromCursor(); + + if (!PCURRMONITOR) { + Debug::log(ERR, "focusWorkspaceOnCurrentMonitor monitor doesn't exist!"); + return; + } + + auto PWORKSPACE = g_pCompositor->getWorkspaceByID(WORKSPACEID); + + if (!PWORKSPACE) { + PWORKSPACE = g_pCompositor->createNewWorkspace(WORKSPACEID, PCURRMONITOR->ID); + // we can skip the moving, since it's already on the current monitor + changeworkspace(PWORKSPACE->getConfigName()); + return; + } + + if (PWORKSPACE->m_iMonitorID != PCURRMONITOR->ID) { + const auto POLDMONITOR = g_pCompositor->getMonitorFromID(PWORKSPACE->m_iMonitorID); + if (!POLDMONITOR) { // wat + Debug::log(ERR, "focusWorkspaceOnCurrentMonitor old monitor doesn't exist!"); + return; + } + if (POLDMONITOR->activeWorkspace == WORKSPACEID) { + g_pCompositor->swapActiveWorkspaces(POLDMONITOR, PCURRMONITOR); + return; + } else { + g_pCompositor->moveWorkspaceToMonitor(PWORKSPACE, PCURRMONITOR, true); + } + } + + changeworkspace(PWORKSPACE->getConfigName()); +} + void CKeybindManager::toggleSpecialWorkspace(std::string args) { static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; @@ -1806,11 +1852,11 @@ void CKeybindManager::dpms(std::string arg) { if (!port.empty() && m->szName != port) continue; - wlr_output_enable(m->output, enable); + wlr_output_state_set_enabled(m->state.wlr(), enable); m->dpmsStatus = enable; - if (!wlr_output_commit(m->output)) { + if (!m->state.commit()) { Debug::log(ERR, "Couldn't commit output {}", m->szName); } diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp index 631b0147..7ca43ec1 100644 --- a/src/managers/KeybindManager.hpp +++ b/src/managers/KeybindManager.hpp @@ -41,6 +41,7 @@ struct SPressedKeyWithMods { xkb_keysym_t keysym = 0; uint32_t keycode = 0; uint32_t modmaskAtPressTime = 0; + bool sent = false; }; class CKeybindManager { @@ -133,6 +134,7 @@ class CKeybindManager { static void exitHyprland(std::string); static void moveCurrentWorkspaceToMonitor(std::string); static void moveWorkspaceToMonitor(std::string); + static void focusWorkspaceOnCurrentMonitor(std::string); static void toggleSpecialWorkspace(std::string); static void forceRendererReload(std::string); static void resizeActive(std::string); diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 207df738..2c46cf85 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -421,7 +421,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { m_bLastFocusOnLS = false; return; // don't enter any new surfaces } else { - if (((FOLLOWMOUSE != 3 && allowKeyboardRefocus) && (*PMOUSEREFOCUS || m_pLastMouseFocus != pFoundWindow)) || refocus) { + if (allowKeyboardRefocus && ((FOLLOWMOUSE != 3 && (*PMOUSEREFOCUS || m_pLastMouseFocus != pFoundWindow)) || refocus)) { m_pLastMouseFocus = pFoundWindow; g_pCompositor->focusWindow(pFoundWindow, foundSurface); } @@ -695,7 +695,8 @@ void CInputManager::onMouseWheel(wlr_pointer_axis_event* e) { return; } - wlr_seat_pointer_notify_axis(g_pCompositor->m_sSeat.seat, e->time_msec, e->orientation, factor * e->delta, std::round(factor * e->delta_discrete), e->source); + wlr_seat_pointer_notify_axis(g_pCompositor->m_sSeat.seat, e->time_msec, e->orientation, factor * e->delta, std::round(factor * e->delta_discrete), e->source, + WLR_AXIS_RELATIVE_DIRECTION_IDENTICAL); } Vector2D CInputManager::getMouseCoordsInternal() { @@ -1185,6 +1186,9 @@ void CInputManager::onKeyboardKey(wlr_keyboard_key_event* e, SKeyboard* pKeyboar updateKeyboardsLeds(pKeyboard->keyboard); } + + if (m_bExitTriggered) + g_pCompositor->cleanup(); } void CInputManager::onKeyboardMod(void* data, SKeyboard* pKeyboard) { @@ -1457,12 +1461,16 @@ void CInputManager::newTouchDevice(wlr_input_device* pDevice) { } void CInputManager::setTouchDeviceConfigs(STouchDevice* dev) { - auto setConfig = [&](STouchDevice* const PTOUCHDEV) -> void { if (wlr_input_device_is_libinput(PTOUCHDEV->pWlrDevice)) { const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(PTOUCHDEV->pWlrDevice); - const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(PTOUCHDEV->name, "transform", "input:touchdevice:transform"), 0, 7); + const auto ENABLED = g_pConfigManager->getDeviceInt(PTOUCHDEV->name, "enabled", "input:touchdevice:enabled"); + const auto mode = ENABLED ? LIBINPUT_CONFIG_SEND_EVENTS_ENABLED : LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; + if (libinput_device_config_send_events_get_mode(LIBINPUTDEV) != mode) + libinput_device_config_send_events_set_mode(LIBINPUTDEV, mode); + + const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(PTOUCHDEV->name, "transform", "input:touchdevice:transform"), 0, 7); if (libinput_device_config_calibration_has_matrix(LIBINPUTDEV)) libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]); diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index 5fa5824e..e32e27f8 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -142,25 +142,28 @@ class CInputManager { // Switches std::list m_lSwitches; - void newTabletTool(wlr_input_device*); - void newTabletPad(wlr_input_device*); - void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false); - void newIdleInhibitor(wlr_idle_inhibitor_v1*); - void recheckIdleInhibitorStatus(); + // Exclusive layer surfaces + std::deque m_dExclusiveLSes; - void onSwipeBegin(wlr_pointer_swipe_begin_event*); - void onSwipeEnd(wlr_pointer_swipe_end_event*); - void onSwipeUpdate(wlr_pointer_swipe_update_event*); + void newTabletTool(wlr_input_device*); + void newTabletPad(wlr_input_device*); + void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false); + void newIdleInhibitor(wlr_idle_inhibitor_v1*); + void recheckIdleInhibitorStatus(); - SSwipeGesture m_sActiveSwipe; + void onSwipeBegin(wlr_pointer_swipe_begin_event*); + void onSwipeEnd(wlr_pointer_swipe_end_event*); + void onSwipeUpdate(wlr_pointer_swipe_update_event*); - SKeyboard* m_pActiveKeyboard = nullptr; + SSwipeGesture m_sActiveSwipe; - CTimer m_tmrLastCursorMovement; + SKeyboard* m_pActiveKeyboard = nullptr; - CInputMethodRelay m_sIMERelay; + CTimer m_tmrLastCursorMovement; - void updateKeyboardsLeds(wlr_input_device* pKeyboard); + CInputMethodRelay m_sIMERelay; + + void updateKeyboardsLeds(wlr_input_device* pKeyboard); // for shared mods uint32_t accumulateModsFromAllKBs(); @@ -243,6 +246,8 @@ class CInputManager { void restoreCursorIconToApp(); // no-op if restored + bool m_bExitTriggered = false; // for exit dispatcher + friend class CKeybindManager; friend class CWLSurface; }; diff --git a/src/managers/input/Swipe.cpp b/src/managers/input/Swipe.cpp index f345e4f5..38ec8aa2 100644 --- a/src/managers/input/Swipe.cpp +++ b/src/managers/input/Swipe.cpp @@ -265,7 +265,7 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) { PWORKSPACE->m_bForceRendering = true; PWORKSPACE->m_fAlpha.setValueAndWarp(1.f); - if (workspaceIDLeft != workspaceIDRight) { + if (workspaceIDLeft != workspaceIDRight && workspaceIDRight != m_sActiveSwipe.pWorkspaceBegin->m_iID) { const auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); if (PWORKSPACER) { @@ -305,7 +305,7 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) { PWORKSPACE->m_bForceRendering = true; PWORKSPACE->m_fAlpha.setValueAndWarp(1.f); - if (workspaceIDLeft != workspaceIDRight) { + if (workspaceIDLeft != workspaceIDRight && workspaceIDLeft != m_sActiveSwipe.pWorkspaceBegin->m_iID) { const auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft); if (PWORKSPACEL) { diff --git a/src/plugins/HookSystem.cpp b/src/plugins/HookSystem.cpp index a7625ffe..ca38fe95 100644 --- a/src/plugins/HookSystem.cpp +++ b/src/plugins/HookSystem.cpp @@ -74,9 +74,10 @@ CFunctionHook::SAssembly CFunctionHook::fixInstructionProbeRIPCalls(const SInstr std::string code = probe.assembly.substr(lastAsmNewline, probe.assembly.find("\n", lastAsmNewline) - lastAsmNewline); if (code.contains("%rip")) { CVarList tokens{code, 0, 's'}; - size_t plusPresent = tokens[1][0] == '+' ? 1 : 0; - std::string addr = tokens[1].substr(plusPresent, tokens[1].find("(%rip)") - plusPresent); - const uint64_t OFFSET = configStringToInt(addr); + size_t plusPresent = tokens[1][0] == '+' ? 1 : 0; + size_t minusPresent = tokens[1][0] == '-' ? 1 : 0; + std::string addr = tokens[1].substr((plusPresent || minusPresent), tokens[1].find("(%rip)") - (plusPresent || minusPresent)); + const uint64_t OFFSET = (minusPresent ? -1 : 1) * configStringToInt(addr); if (OFFSET == 0) return {}; const uint64_t DESTINATION = currentAddress + OFFSET + len; diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index b4d2b0e9..e8414dbd 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -189,16 +189,39 @@ bool CHyprOpenGLImpl::passRequiresIntrospection(CMonitor* pMonitor) { void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, CFramebuffer* fb) { m_RenderData.pMonitor = pMonitor; +#ifndef GLES2 + + const GLenum RESETSTATUS = glGetGraphicsResetStatus(); + if (RESETSTATUS != GL_NO_ERROR) { + std::string errStr = ""; + switch (RESETSTATUS) { + case GL_GUILTY_CONTEXT_RESET: errStr = "GL_GUILTY_CONTEXT_RESET"; break; + case GL_INNOCENT_CONTEXT_RESET: errStr = "GL_INNOCENT_CONTEXT_RESET"; break; + case GL_UNKNOWN_CONTEXT_RESET: errStr = "GL_UNKNOWN_CONTEXT_RESET"; break; + default: errStr = "UNKNOWN??"; break; + } + RASSERT(false, "Aborting, glGetGraphicsResetStatus returned {}. Cannot continue until proper GPU reset handling is implemented.", errStr); + return; + } + +#endif + TRACY_GPU_ZONE("RenderBegin"); glViewport(0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y); matrixProjection(m_RenderData.projection, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, WL_OUTPUT_TRANSFORM_NORMAL); + if (m_mMonitorRenderResources.contains(pMonitor) && m_mMonitorRenderResources.at(pMonitor).offloadFB.m_vSize != pMonitor->vecPixelSize) + destroyMonitorResources(pMonitor); + m_RenderData.pCurrentMonData = &m_mMonitorRenderResources[pMonitor]; + if (!m_RenderData.pCurrentMonData->m_bShadersInitialized) + initShaders(); + // ensure a framebuffer for the monitor exists - if (!m_mMonitorRenderResources.contains(pMonitor) || m_RenderData.pCurrentMonData->offloadFB.m_vSize != pMonitor->vecPixelSize) { + if (m_RenderData.pCurrentMonData->offloadFB.m_vSize != pMonitor->vecPixelSize) { m_RenderData.pCurrentMonData->stencilTex.allocate(); m_RenderData.pCurrentMonData->offloadFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex; @@ -210,16 +233,11 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, CFramebuffer* m_RenderData.pCurrentMonData->mirrorFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat); m_RenderData.pCurrentMonData->mirrorSwapFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat); m_RenderData.pCurrentMonData->offMainFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat); - - createBGTextureForMonitor(pMonitor); } if (m_RenderData.pCurrentMonData->monitorMirrorFB.isAllocated() && m_RenderData.pMonitor->mirrors.empty()) m_RenderData.pCurrentMonData->monitorMirrorFB.release(); - if (!m_RenderData.pCurrentMonData->m_bShadersInitialized) - initShaders(); - m_RenderData.damage.set(*pDamage); m_bFakeFrame = fb; @@ -312,6 +330,15 @@ void CHyprOpenGLImpl::end() { m_RenderData.mouseZoomFactor = 1.f; m_RenderData.mouseZoomUseMouse = true; m_RenderData.forceIntrospection = false; + m_RenderData.currentFB = nullptr; + m_RenderData.mainFB = nullptr; + m_RenderData.outFB = nullptr; + + // check for gl errors + const GLenum ERR = glGetError(); + + if (ERR == GL_CONTEXT_LOST) /* We don't have infra to recover from this */ + RASSERT(false, "glGetError at Opengl::end() returned GL_CONTEXT_LOST. Cannot continue until proper GPU reset handling is implemented."); } void CHyprOpenGLImpl::initShaders() { @@ -723,7 +750,7 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox* alpha = std::clamp(alpha, 0.f, 1.f); - if (m_RenderData.damage.empty()) + if (damage->empty()) return; CBox newBox = *pBox; @@ -1887,10 +1914,10 @@ void CHyprOpenGLImpl::renderMirrored() { renderTexture(PFB->m_cTex, &monbox, 1.f, 0, false, false); } -void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY) { +void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY, const Vector2D& size) { cairo_select_font_face(CAIRO, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); - const auto FONTSIZE = (int)(m_RenderData.pMonitor->vecPixelSize.y / 76); + const auto FONTSIZE = (int)(size.y / 76); cairo_set_font_size(CAIRO, FONTSIZE); cairo_set_source_rgba(CAIRO, 1.0, 1.0, 1.0, 0.32); @@ -1898,7 +1925,7 @@ void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const cairo_text_extents_t textExtents; cairo_text_extents(CAIRO, g_pCompositor->m_szCurrentSplash.c_str(), &textExtents); - cairo_move_to(CAIRO, (m_RenderData.pMonitor->vecPixelSize.x - textExtents.width) / 2.0, m_RenderData.pMonitor->vecPixelSize.y - textExtents.height + offsetY); + cairo_move_to(CAIRO, (size.x - textExtents.width) / 2.0, size.y - textExtents.height + offsetY); cairo_show_text(CAIRO, g_pCompositor->m_szCurrentSplash.c_str()); @@ -1910,109 +1937,123 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(CMonitor* pMonitor) { static auto* const PRENDERTEX = &g_pConfigManager->getConfigValuePtr("misc:disable_hyprland_logo")->intValue; static auto* const PNOSPLASH = &g_pConfigManager->getConfigValuePtr("misc:disable_splash_rendering")->intValue; - static auto* const PFORCEHYPRCHAN = &g_pConfigManager->getConfigValuePtr("misc:force_hypr_chan")->intValue; static auto* const PFORCEWALLPAPER = &g_pConfigManager->getConfigValuePtr("misc:force_default_wallpaper")->intValue; const auto FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, static_cast(-1L), static_cast(2L)); + static std::string texPath = ""; + if (*PRENDERTEX) return; // release the last tex if exists - const auto PTEX = &m_mMonitorBGTextures[pMonitor]; - PTEX->destroyTexture(); + const auto PFB = &m_mMonitorBGFBs[pMonitor]; + PFB->release(); - PTEX->allocate(); + PFB->alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat); Debug::log(LOG, "Allocated texture for BGTex"); // TODO: use relative paths to the installation // or configure the paths at build time - std::string texPath = "/usr/share/hyprland/wall_"; - std::string prefixes[] = {"", "anime_", "anime2_"}; + if (texPath.empty()) { + texPath = "/usr/share/hyprland/wall"; - // get the adequate tex - if (FORCEWALLPAPER == -1) { - std::random_device dev; - std::mt19937 engine(dev()); - std::uniform_int_distribution<> distribution(0, 2); - std::uniform_int_distribution<> distribution_anime(1, 2); + // get the adequate tex + if (FORCEWALLPAPER == -1) { + std::mt19937_64 engine(time(nullptr)); + std::uniform_int_distribution<> distribution(0, 2); - if (PFORCEHYPRCHAN) - texPath += prefixes[distribution_anime(engine)]; - else - texPath += prefixes[distribution(engine)]; - } else - texPath += prefixes[FORCEWALLPAPER]; + texPath += std::to_string(distribution(engine)); + } else + texPath += std::to_string(std::clamp(*PFORCEWALLPAPER, (int64_t)0, (int64_t)2)); - Vector2D textureSize; - if (pMonitor->vecTransformedSize.x > 3850) { - textureSize = Vector2D(7680, 4320); - texPath += "8K.png"; - } else if (pMonitor->vecTransformedSize.x > 1930) { - textureSize = Vector2D(3840, 2160); - texPath += "4K.png"; - } else { - textureSize = Vector2D(1920, 1080); - texPath += "2K.png"; + texPath += ".png"; + + // check if wallpapers exist + if (!std::filesystem::exists(texPath)) { + // try local + texPath = texPath.substr(0, 5) + "local/" + texPath.substr(5); + + if (!std::filesystem::exists(texPath)) + return; // the texture will be empty, oh well. We'll clear with a solid color anyways. + } } - // check if wallpapers exist - if (!std::filesystem::exists(texPath)) { - // try local - texPath = texPath.substr(0, 5) + "local/" + texPath.substr(5); + // create a new one with cairo + CTexture tex; - if (!std::filesystem::exists(texPath)) - return; // the texture will be empty, oh well. We'll clear with a solid color anyways. - } + const auto CAIROISURFACE = cairo_image_surface_create_from_png(texPath.c_str()); + const auto CAIROFORMAT = cairo_image_surface_get_format(CAIROISURFACE); - PTEX->m_vSize = textureSize; + tex.allocate(); + const Vector2D IMAGESIZE = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)}; // calc the target box const double MONRATIO = m_RenderData.pMonitor->vecTransformedSize.x / m_RenderData.pMonitor->vecTransformedSize.y; - const double WPRATIO = 1.77; + const double WPRATIO = IMAGESIZE.x / IMAGESIZE.y; Vector2D origin; double scale; if (MONRATIO > WPRATIO) { - scale = m_RenderData.pMonitor->vecTransformedSize.x / PTEX->m_vSize.x; + scale = m_RenderData.pMonitor->vecTransformedSize.x / IMAGESIZE.x; - origin.y = (m_RenderData.pMonitor->vecTransformedSize.y - PTEX->m_vSize.y * scale) / 2.0; + origin.y = (m_RenderData.pMonitor->vecTransformedSize.y - IMAGESIZE.y * scale) / 2.0; } else { - scale = m_RenderData.pMonitor->vecTransformedSize.y / PTEX->m_vSize.y; + scale = m_RenderData.pMonitor->vecTransformedSize.y / IMAGESIZE.y; - origin.x = (m_RenderData.pMonitor->vecTransformedSize.x - PTEX->m_vSize.x * scale) / 2.0; + origin.x = (m_RenderData.pMonitor->vecTransformedSize.x - IMAGESIZE.x * scale) / 2.0; } - CBox box = {origin.x, origin.y, PTEX->m_vSize.x * scale, PTEX->m_vSize.y * scale}; + const Vector2D scaledSize = IMAGESIZE * scale; - m_mMonitorRenderResources[pMonitor].backgroundTexBox = box; + const auto CAIROSURFACE = cairo_image_surface_create(CAIROFORMAT, scaledSize.x, scaledSize.y); + const auto CAIRO = cairo_create(CAIROSURFACE); - // create a new one with cairo - const auto CAIROSURFACE = cairo_image_surface_create_from_png(texPath.c_str()); - const auto CAIRO = cairo_create(CAIROSURFACE); + cairo_set_antialias(CAIRO, CAIRO_ANTIALIAS_GOOD); + cairo_scale(CAIRO, scale, scale); + cairo_rectangle(CAIRO, 0, 0, 100, 100); + cairo_set_source_surface(CAIRO, CAIROISURFACE, 0, 0); + cairo_paint(CAIRO); - // scale it to fit the current monitor - cairo_scale(CAIRO, textureSize.x / pMonitor->vecTransformedSize.x, textureSize.y / pMonitor->vecTransformedSize.y); - - // render splash on wallpaper if (!*PNOSPLASH) - renderSplash(CAIRO, CAIROSURFACE, origin.y * WPRATIO / MONRATIO); + renderSplash(CAIRO, CAIROSURFACE, origin.y * WPRATIO / MONRATIO * scale, IMAGESIZE); + + cairo_surface_flush(CAIROSURFACE); + + CBox box = {origin.x, origin.y, IMAGESIZE.x * scale, IMAGESIZE.y * scale}; + tex.m_vSize = IMAGESIZE * scale; // copy the data to an OpenGL texture we have - const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); - glBindTexture(GL_TEXTURE_2D, PTEX->m_iTexID); + const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA; + const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA; + const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE; + + const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); + glBindTexture(GL_TEXTURE_2D, tex.m_iTexID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); #ifndef GLES2 - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + } #endif - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSize.x, textureSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); + glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex.m_vSize.x, tex.m_vSize.y, 0, glFormat, glType, DATA); cairo_surface_destroy(CAIROSURFACE); + cairo_surface_destroy(CAIROISURFACE); cairo_destroy(CAIRO); + // render the texture to our fb + PFB->bind(); + CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX}; + renderTextureInternalWithDamage(tex, &box, 1.0, &fakeDamage); + + // bind back + if (m_RenderData.currentFB) + m_RenderData.currentFB->bind(); + Debug::log(LOG, "Background created for monitor {}", pMonitor->szName); } @@ -2021,15 +2062,19 @@ void CHyprOpenGLImpl::clearWithTex() { TRACY_GPU_ZONE("RenderClearWithTex"); - auto TEXIT = m_mMonitorBGTextures.find(m_RenderData.pMonitor); + auto TEXIT = m_mMonitorBGFBs.find(m_RenderData.pMonitor); - if (TEXIT == m_mMonitorBGTextures.end()) { + if (TEXIT == m_mMonitorBGFBs.end()) { createBGTextureForMonitor(m_RenderData.pMonitor); - TEXIT = m_mMonitorBGTextures.find(m_RenderData.pMonitor); + TEXIT = m_mMonitorBGFBs.find(m_RenderData.pMonitor); } - if (TEXIT != m_mMonitorBGTextures.end()) - renderTexturePrimitive(TEXIT->second, &m_mMonitorRenderResources[m_RenderData.pMonitor].backgroundTexBox); + if (TEXIT != m_mMonitorBGFBs.end()) { + CBox monbox = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y}; + m_bEndFrame = true; + renderTexture(TEXIT->second.m_cTex, &monbox, 1); + m_bEndFrame = false; + } } void CHyprOpenGLImpl::destroyMonitorResources(CMonitor* pMonitor) { @@ -2047,10 +2092,10 @@ void CHyprOpenGLImpl::destroyMonitorResources(CMonitor* pMonitor) { g_pHyprOpenGL->m_mMonitorRenderResources.erase(RESIT); } - auto TEXIT = g_pHyprOpenGL->m_mMonitorBGTextures.find(pMonitor); - if (TEXIT != g_pHyprOpenGL->m_mMonitorBGTextures.end()) { - TEXIT->second.destroyTexture(); - g_pHyprOpenGL->m_mMonitorBGTextures.erase(TEXIT); + auto TEXIT = g_pHyprOpenGL->m_mMonitorBGFBs.find(pMonitor); + if (TEXIT != g_pHyprOpenGL->m_mMonitorBGFBs.end()) { + TEXIT->second.release(); + g_pHyprOpenGL->m_mMonitorBGFBs.erase(TEXIT); } Debug::log(LOG, "Monitor {} -> destroyed all render data", pMonitor->szName); diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 039015d2..2385fd02 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -71,8 +71,6 @@ struct SMonitorRenderData { bool blurFBDirty = true; bool blurFBShouldRender = false; - CBox backgroundTexBox; - // Shaders bool m_bShadersInitialized = false; CShader m_shQUAD; @@ -191,7 +189,7 @@ class CHyprOpenGLImpl { std::unordered_map m_mWindowFramebuffers; std::unordered_map m_mLayerFramebuffers; std::unordered_map m_mMonitorRenderResources; - std::unordered_map m_mMonitorBGTextures; + std::unordered_map m_mMonitorBGFBs; struct { PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr; @@ -229,7 +227,7 @@ class CHyprOpenGLImpl { void renderTextureInternalWithDamage(const CTexture&, CBox* pBox, float a, CRegion* damage, CCornerRadiiData radii = 0, bool discardOpaque = false, bool noAA = false, bool allowCustomUV = false, bool allowDim = false); void renderTexturePrimitive(const CTexture& tex, CBox* pBox); - void renderSplash(cairo_t* const, cairo_surface_t* const, double); + void renderSplash(cairo_t* const, cairo_surface_t* const, double offset, const Vector2D& size); void preBlurForCurrentMonitor(); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 6c2d0d72..dd190f4b 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -9,8 +9,6 @@ extern "C" { } CHyprRenderer::CHyprRenderer() { - if (envEnabled("WLR_DRM_NO_ATOMIC")) - m_bTearingEnvSatisfied = true; if (g_pCompositor->m_sWLRSession) { wlr_device* dev; @@ -107,19 +105,22 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) if (windowBox.width <= 1 || windowBox.height <= 1) return; // invisible - g_pHyprRenderer->calculateUVForSurface(RDATA->pWindow, surface, RDATA->surface == surface); - windowBox.scale(RDATA->pMonitor->scale); windowBox.round(); - // check for fractional scale surfaces misaligning the buffer size - // in those cases it's better to just force nearest neighbor - // as long as the window is not animated. During those it'd look weird - const auto NEARESTNEIGHBORSET = g_pHyprOpenGL->m_RenderData.useNearestNeighbor; - if (std::floor(RDATA->pMonitor->scale) != RDATA->pMonitor->scale /* Fractional */ && surface->current.scale == 1 /* fs protocol */ && + const bool MISALIGNEDFSV1 = std::floor(RDATA->pMonitor->scale) != RDATA->pMonitor->scale /* Fractional */ && surface->current.scale == 1 /* fs protocol */ && windowBox.size() != Vector2D{surface->current.buffer_width, surface->current.buffer_height} /* misaligned */ && DELTALESSTHAN(windowBox.width, surface->current.buffer_width, 3) && DELTALESSTHAN(windowBox.height, surface->current.buffer_height, 3) /* off by one-or-two */ && - (!RDATA->pWindow || (!RDATA->pWindow->m_vRealSize.isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */) + (!RDATA->pWindow || (!RDATA->pWindow->m_vRealSize.isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */; + + g_pHyprRenderer->calculateUVForSurface(RDATA->pWindow, surface, RDATA->surface == surface, windowBox.size(), MISALIGNEDFSV1); + + // check for fractional scale surfaces misaligning the buffer size + // in those cases it's better to just force nearest neighbor + // as long as the window is not animated. During those it'd look weird. + // UV will fixup it as well + const auto NEARESTNEIGHBORSET = g_pHyprOpenGL->m_RenderData.useNearestNeighbor; + if (MISALIGNEDFSV1) g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true; CCornerRadiiData radii = RDATA->cornerRadii; @@ -290,11 +291,14 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, CWorksp continue; } - if (w->m_iWorkspaceID != pMonitor->activeWorkspace || !w->m_bIsFullscreen) + if (!w->m_bIsFullscreen) continue; renderWindow(w.get(), pMonitor, time, pWorkspace->m_efFullscreenMode != FULLSCREEN_FULL, RENDER_PASS_ALL); + if (w->m_iWorkspaceID != pWorkspace->m_iID) + continue; + pWorkspaceWindow = w.get(); } @@ -453,7 +457,7 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, CMonitor* pMonitor, timespec* // clip box for animated offsets const Vector2D PREOFFSETPOS = {renderdata.x, renderdata.y}; - if (!ignorePosition && pWindow->m_bIsFloating && !pWindow->m_bPinned) { + if (!ignorePosition && pWindow->m_bIsFloating && !pWindow->m_bPinned && !pWindow->m_bIsFullscreen) { Vector2D offset; if (PWORKSPACE->m_vRenderOffset.vec().x != 0) { @@ -805,7 +809,7 @@ void CHyprRenderer::renderLockscreen(CMonitor* pMonitor, timespec* now) { } } -void CHyprRenderer::calculateUVForSurface(CWindow* pWindow, wlr_surface* pSurface, bool main) { +void CHyprRenderer::calculateUVForSurface(CWindow* pWindow, wlr_surface* pSurface, bool main, const Vector2D& projSize, bool fixMisalignedFSV1) { if (!pWindow || !pWindow->m_bIsX11) { Vector2D uvTL; Vector2D uvBR = Vector2D(1, 1); @@ -827,6 +831,15 @@ void CHyprRenderer::calculateUVForSurface(CWindow* pWindow, wlr_surface* pSurfac } } + if (projSize != Vector2D{} && fixMisalignedFSV1) { + // instead of nearest_neighbor (we will repeat / skip) + // just cut off / expand surface + const Vector2D PIXELASUV = Vector2D{1, 1} / Vector2D{pSurface->buffer->texture->width, pSurface->buffer->texture->height}; + const Vector2D MISALIGNMENT = Vector2D{pSurface->buffer->texture->width, pSurface->buffer->texture->height} - projSize; + if (MISALIGNMENT != Vector2D{}) + uvBR -= MISALIGNMENT * PIXELASUV; + } + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = uvTL; g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = uvBR; @@ -902,9 +915,9 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) { return false; // finally, we should be GTG. - wlr_output_attach_buffer(pMonitor->output, &PSURFACE->buffer->base); + wlr_output_state_set_buffer(pMonitor->state.wlr(), &PSURFACE->buffer->base); - if (!wlr_output_test(pMonitor->output)) + if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) return false; timespec now; @@ -912,7 +925,7 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) { wlr_surface_send_frame_done(PSURFACE, &now); wlr_presentation_surface_scanned_out_on_output(PSURFACE, pMonitor->output); - if (wlr_output_commit(pMonitor->output)) { + if (pMonitor->state.commit()) { if (!m_pLastScanout) { m_pLastScanout = PCANDIDATE; Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE, PCANDIDATE->m_szTitle); @@ -1013,14 +1026,15 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { const auto PGAMMACTRL = wlr_gamma_control_manager_v1_get_control(g_pCompositor->m_sWLRGammaCtrlMgr, pMonitor->output); - if (!wlr_gamma_control_v1_apply(PGAMMACTRL, &pMonitor->output->pending)) { + if (!wlr_gamma_control_v1_apply(PGAMMACTRL, pMonitor->state.wlr())) { Debug::log(ERR, "Could not apply gamma control to {}", pMonitor->szName); return; } - if (!wlr_output_test(pMonitor->output)) { + if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { Debug::log(ERR, "Output test failed for setting gamma to {}", pMonitor->szName); - wlr_output_rollback(pMonitor->output); + // aka rollback + wlr_gamma_control_v1_apply(nullptr, pMonitor->state.wlr()); wlr_gamma_control_v1_send_failed_and_destroy(PGAMMACTRL); } } @@ -1149,6 +1163,8 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { if (UNLOCK_SC) wlr_output_lock_software_cursors(pMonitor->output, false); + pMonitor->state.clear(); + return; } @@ -1235,9 +1251,9 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { EMIT_HOOK_EVENT("render", RENDER_POST); - pMonitor->output->pending.tearing_page_flip = shouldTear; + pMonitor->state.wlr()->tearing_page_flip = shouldTear; - if (!wlr_output_commit(pMonitor->output)) { + if (!pMonitor->state.commit()) { if (UNLOCK_SC) wlr_output_lock_software_cursors(pMonitor->output, false); @@ -1324,6 +1340,8 @@ void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool std::string commandForCfg = ""; const auto OUTPUT = head->state.output; + const auto PMONITOR = g_pCompositor->getMonitorFromOutput(OUTPUT); + RASSERT(PMONITOR, "nullptr monitor in outputMgrApplyTest"); commandForCfg += std::string(OUTPUT->name) + ","; @@ -1334,7 +1352,7 @@ void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool continue; } - wlr_output_enable(OUTPUT, head->state.enabled); + wlr_output_state_set_enabled(PMONITOR->state.wlr(), head->state.enabled); if (head->state.mode) commandForCfg += @@ -1348,10 +1366,10 @@ void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool if (!test) { g_pConfigManager->parseKeyword("monitor", commandForCfg, true); - wlr_output_state_set_adaptive_sync_enabled(&OUTPUT->pending, head->state.adaptive_sync_enabled); + wlr_output_state_set_adaptive_sync_enabled(PMONITOR->state.wlr(), head->state.adaptive_sync_enabled); } - ok = wlr_output_test(OUTPUT); + ok = wlr_output_test_state(OUTPUT, PMONITOR->state.wlr()); if (!ok) break; @@ -1572,8 +1590,7 @@ void CHyprRenderer::damageSurface(wlr_surface* pSurface, double x, double y, dou y += CORRECTION.y; } - CRegion damageBox; - wlr_surface_get_effective_damage(pSurface, damageBox.pixman()); + CRegion damageBox{&pSurface->buffer_damage}; if (scale != 1.0) wlr_region_scale(damageBox.pixman(), damageBox.pixman(), scale); @@ -1750,6 +1767,9 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR return true; } + const auto WAS10B = pMonitor->enabled10bit; + const auto OLDRES = pMonitor->vecPixelSize; + // Needed in case we are switching from a custom modeline to a standard mode pMonitor->customDrmMode = {}; bool autoScale = false; @@ -1762,10 +1782,10 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR pMonitor->scale = DEFAULTSCALE; } - wlr_output_set_scale(pMonitor->output, pMonitor->scale); + wlr_output_state_set_scale(pMonitor->state.wlr(), pMonitor->scale); pMonitor->setScale = pMonitor->scale; - wlr_output_set_transform(pMonitor->output, pMonitorRule->transform); + wlr_output_state_set_transform(pMonitor->state.wlr(), pMonitorRule->transform); pMonitor->transform = pMonitorRule->transform; const auto WLRREFRESHRATE = (wlr_backend_is_wl(pMonitor->output->backend) || wlr_backend_is_x11(pMonitor->output->backend)) ? 0 : pMonitorRule->refreshRate * 1000; @@ -1780,9 +1800,9 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR // if delta of refresh rate, w and h chosen and mode is < 1 we accept it if (DELTALESSTHAN(mode->width, pMonitorRule->resolution.x, 1) && DELTALESSTHAN(mode->height, pMonitorRule->resolution.y, 1) && DELTALESSTHAN(mode->refresh / 1000.f, pMonitorRule->refreshRate, 1)) { - wlr_output_set_mode(pMonitor->output, mode); + wlr_output_state_set_mode(pMonitor->state.wlr(), mode); - if (!wlr_output_test(pMonitor->output)) { + if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { Debug::log(LOG, "Monitor {}: REJECTED available mode: {}x{}@{:2f}!", pMonitor->output->name, mode->width, mode->height, mode->refresh / 1000.f); continue; } @@ -1800,11 +1820,11 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } if (!found) { - wlr_output_set_custom_mode(pMonitor->output, (int)pMonitorRule->resolution.x, (int)pMonitorRule->resolution.y, WLRREFRESHRATE); + wlr_output_state_set_custom_mode(pMonitor->state.wlr(), (int)pMonitorRule->resolution.x, (int)pMonitorRule->resolution.y, WLRREFRESHRATE); pMonitor->vecSize = pMonitorRule->resolution; pMonitor->refreshRate = pMonitorRule->refreshRate; - if (!wlr_output_test(pMonitor->output)) { + if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { Debug::log(ERR, "Custom resolution FAILED, falling back to preferred"); const auto PREFERREDMODE = wlr_output_preferred_mode(pMonitor->output); @@ -1816,7 +1836,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } // Preferred is valid - wlr_output_set_mode(pMonitor->output, PREFERREDMODE); + wlr_output_state_set_mode(pMonitor->state.wlr(), PREFERREDMODE); Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name, pMonitorRule->resolution, (float)pMonitorRule->refreshRate, PREFERREDMODE->width, PREFERREDMODE->height, PREFERREDMODE->refresh / 1000.f); @@ -1838,7 +1858,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } else { auto* mode = wlr_drm_connector_add_mode(pMonitor->output, &pMonitorRule->drmMode); if (mode) { - wlr_output_set_mode(pMonitor->output, mode); + wlr_output_state_set_mode(pMonitor->state.wlr(), mode); pMonitor->customDrmMode = pMonitorRule->drmMode; } else { Debug::log(ERR, "wlr_drm_connector_add_mode failed"); @@ -1846,13 +1866,13 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } } } else { - wlr_output_set_custom_mode(pMonitor->output, (int)pMonitorRule->resolution.x, (int)pMonitorRule->resolution.y, WLRREFRESHRATE); + wlr_output_state_set_custom_mode(pMonitor->state.wlr(), (int)pMonitorRule->resolution.x, (int)pMonitorRule->resolution.y, WLRREFRESHRATE); } pMonitor->vecSize = pMonitorRule->resolution; pMonitor->refreshRate = pMonitorRule->refreshRate; - if (fail || !wlr_output_test(pMonitor->output)) { + if (fail || !wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { Debug::log(ERR, "Custom resolution FAILED, falling back to preferred"); const auto PREFERREDMODE = wlr_output_preferred_mode(pMonitor->output); @@ -1864,7 +1884,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } // Preferred is valid - wlr_output_set_mode(pMonitor->output, PREFERREDMODE); + wlr_output_state_set_mode(pMonitor->state.wlr(), PREFERREDMODE); Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name, pMonitorRule->resolution, (float)pMonitorRule->refreshRate, PREFERREDMODE->width, PREFERREDMODE->height, PREFERREDMODE->refresh / 1000.f); @@ -1888,8 +1908,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR if (pMonitorRule->resolution == Vector2D(-1, -1)) { wl_list_for_each(mode, &pMonitor->output->modes, link) { if ((mode->width >= currentWidth && mode->height >= currentHeight && mode->refresh >= (currentRefresh - 1000.f)) || mode->refresh > (currentRefresh + 3000.f)) { - wlr_output_set_mode(pMonitor->output, mode); - if (wlr_output_test(pMonitor->output)) { + wlr_output_state_set_mode(pMonitor->state.wlr(), mode); + if (wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { currentWidth = mode->width; currentHeight = mode->height; currentRefresh = mode->refresh; @@ -1901,8 +1921,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR wl_list_for_each(mode, &pMonitor->output->modes, link) { if ((mode->width >= currentWidth && mode->height >= currentHeight && mode->refresh >= (currentRefresh - 1000.f)) || (mode->width > currentWidth && mode->height > currentHeight)) { - wlr_output_set_mode(pMonitor->output, mode); - if (wlr_output_test(pMonitor->output)) { + wlr_output_state_set_mode(pMonitor->state.wlr(), mode); + if (wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { currentWidth = mode->width; currentHeight = mode->height; currentRefresh = mode->refresh; @@ -1925,7 +1945,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } // Preferred is valid - wlr_output_set_mode(pMonitor->output, PREFERREDMODE); + wlr_output_state_set_mode(pMonitor->state.wlr(), PREFERREDMODE); Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name, pMonitorRule->resolution, (float)pMonitorRule->refreshRate, PREFERREDMODE->width, PREFERREDMODE->height, PREFERREDMODE->refresh / 1000.f); @@ -1950,9 +1970,9 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR wlr_output_mode* mode; wl_list_for_each(mode, &pMonitor->output->modes, link) { - wlr_output_set_mode(pMonitor->output, mode); + wlr_output_state_set_mode(pMonitor->state.wlr(), mode); - if (!wlr_output_test(pMonitor->output)) { + if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { Debug::log(LOG, "Monitor {}: REJECTED available mode: {}x{}@{:2f}!", pMonitor->output->name, mode->width, mode->height, mode->refresh / 1000.f); continue; } @@ -1968,7 +1988,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } } else { // Preferred is valid - wlr_output_set_mode(pMonitor->output, PREFERREDMODE); + wlr_output_state_set_mode(pMonitor->state.wlr(), PREFERREDMODE); pMonitor->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height); pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f; @@ -1977,7 +1997,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } } - pMonitor->vrrActive = pMonitor->output->pending.adaptive_sync_enabled; // disabled here, will be tested in CConfigManager::ensureVRR() + pMonitor->vrrActive = pMonitor->state.wlr()->adaptive_sync_enabled // disabled here, will be tested in CConfigManager::ensureVRR() + || pMonitor->createdByUser; // wayland backend doesn't allow for disabling adaptive_sync pMonitor->vecPixelSize = pMonitor->vecSize; @@ -1994,6 +2015,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR Vector2D logicalZero = pMonitor->vecPixelSize / scaleZero; if (logicalZero == logicalZero.round()) { pMonitor->scale = scaleZero; + wlr_output_state_set_scale(pMonitor->state.wlr(), pMonitor->scale); } else { for (size_t i = 1; i < 90; ++i) { double scaleUp = (searchScale + i) / 120.0; @@ -2031,11 +2053,15 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } else pMonitor->scale = searchScale; } + + // for wlroots, that likes flooring, we have to do this. + double logicalX = std::round(pMonitor->vecPixelSize.x / pMonitor->scale); + logicalX += 0.1; + + wlr_output_state_set_scale(pMonitor->state.wlr(), pMonitor->vecPixelSize.x / logicalX); } } - wlr_output_set_scale(pMonitor->output, pMonitor->scale); - // clang-format off static const std::array>, 2> formats{ std::vector>{ /* 10-bit */ @@ -2051,9 +2077,9 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR pMonitor->drmFormat = DRM_FORMAT_INVALID; for (auto& fmt : formats[(int)!pMonitorRule->enable10bit]) { - wlr_output_set_render_format(pMonitor->output, fmt.second); + wlr_output_state_set_render_format(pMonitor->state.wlr(), fmt.second); - if (!wlr_output_test(pMonitor->output)) { + if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { Debug::log(ERR, "output {} failed basic test on format {}", pMonitor->szName, fmt.first); } else { Debug::log(LOG, "output {} succeeded basic test on format {}", pMonitor->szName, fmt.first); @@ -2067,9 +2093,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR pMonitor->enabled10bit = set10bit; - if (!wlr_output_commit(pMonitor->output)) { + if (!pMonitor->state.commit()) Debug::log(ERR, "Couldn't commit output named {}", pMonitor->output->name); - } int x, y; wlr_output_transformed_resolution(pMonitor->output, &x, &y); @@ -2085,14 +2110,21 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR pMonitor->updateMatrix(); - // update renderer (here because it will call rollback, so we cannot do this before committing) - g_pHyprOpenGL->destroyMonitorResources(pMonitor); + if (WAS10B != pMonitor->enabled10bit || OLDRES != pMonitor->vecPixelSize) + g_pHyprOpenGL->destroyMonitorResources(pMonitor); // updato wlroots g_pCompositor->arrangeMonitors(); wlr_damage_ring_set_bounds(&pMonitor->damage, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y); + // Set scale for all surfaces on this monitor, needed for some clients + // but not on unsafe state to avoid crashes + if (!g_pCompositor->m_bUnsafeState) { + for (auto& w : g_pCompositor->m_vWindows) { + w->updateSurfaceScaleTransformDetails(); + } + } // updato us arrangeLayersForMonitor(pMonitor->ID); @@ -2429,7 +2461,7 @@ bool CHyprRenderer::beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode } if (!buffer) { - if (!wlr_output_configure_primary_swapchain(pMonitor->output, &pMonitor->output->pending, &pMonitor->output->swapchain)) + if (!wlr_output_configure_primary_swapchain(pMonitor->output, pMonitor->state.wlr(), &pMonitor->output->swapchain)) return false; m_pCurrentWlrBuffer = wlr_swapchain_acquire(pMonitor->output->swapchain, &m_iLastBufferAge); @@ -2473,7 +2505,7 @@ void CHyprRenderer::endRender() { glFlush(); if (m_eRenderMode == RENDER_MODE_NORMAL) { - wlr_output_state_set_buffer(&PMONITOR->output->pending, m_pCurrentWlrBuffer); + wlr_output_state_set_buffer(PMONITOR->state.wlr(), m_pCurrentWlrBuffer); unsetEGL(); // flush the context } diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index bc914558..f77bf36e 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -57,7 +57,7 @@ class CHyprRenderer { void ensureCursorRenderingMode(); bool shouldRenderCursor(); void setCursorHidden(bool hide); - void calculateUVForSurface(CWindow*, wlr_surface*, bool main = false); + void calculateUVForSurface(CWindow*, wlr_surface*, bool main = false, const Vector2D& projSize = {}, bool fixMisalignedFSV1 = false); std::tuple getRenderTimes(CMonitor* pMonitor); // avg max min void renderLockscreen(CMonitor* pMonitor, timespec* now); void setOccludedForBackLayers(CRegion& region, CWorkspace* pWorkspace); @@ -82,7 +82,6 @@ class CHyprRenderer { CMonitor* m_pMostHzMonitor = nullptr; bool m_bDirectScanoutBlocked = false; bool m_bSoftwareCursorsLocked = false; - bool m_bTearingEnvSatisfied = false; DAMAGETRACKINGMODES damageTrackingModeFromStr(const std::string&); diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp index 61409256..9a74dcd7 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.cpp +++ b/src/render/decorations/CHyprGroupBarDecoration.cpp @@ -26,9 +26,10 @@ CHyprGroupBarDecoration::CHyprGroupBarDecoration(CWindow* pWindow) : IHyprWindow CHyprGroupBarDecoration::~CHyprGroupBarDecoration() {} SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() { - static auto* const PRENDERTITLES = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->intValue; - static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("group:groupbar:font_size")->intValue; - static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue; + static auto* const PHEIGHT = &g_pConfigManager->getConfigValuePtr("group:groupbar:height")->intValue; + static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue; + static auto* const PRENDERTITLES = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->intValue; + static auto* const PGRADIENTS = &g_pConfigManager->getConfigValuePtr("group:groupbar:gradients")->intValue; SDecorationPositioningInfo info; info.policy = DECORATION_POSITION_STICKY; @@ -37,7 +38,7 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() { info.reserved = true; if (*PENABLED && m_pWindow->m_sSpecialRenderData.decorate) - info.desiredExtents = {{0, BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PRENDERTITLES ? *PTITLEFONTSIZE : 0) + 2}, {0, 0}}; + info.desiredExtents = {{0, BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0) + 2}, {0, 0}}; else info.desiredExtents = {{0, 0}, {0, 0}}; @@ -90,6 +91,7 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue; static auto* const PRENDERTITLES = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->intValue; static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("group:groupbar:font_size")->intValue; + static auto* const PHEIGHT = &g_pConfigManager->getConfigValuePtr("group:groupbar:height")->intValue; static auto* const PGRADIENTS = &g_pConfigManager->getConfigValuePtr("group:groupbar:gradients")->intValue; if (!*PENABLED || !m_pWindow->m_sSpecialRenderData.decorate) @@ -99,7 +101,7 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& m_fBarWidth = (ASSIGNEDBOX.w - BAR_HORIZONTAL_PADDING * (barsToDraw - 1)) / barsToDraw; - const auto DESIREDHEIGHT = BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PRENDERTITLES ? *PTITLEFONTSIZE : 0) + 2; + const auto DESIREDHEIGHT = BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0) + 2; if (DESIREDHEIGHT != ASSIGNEDBOX.h) g_pDecorationPositioner->repositionDeco(this); @@ -128,12 +130,18 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& color.a *= a; g_pHyprOpenGL->renderRect(&rect, color); - // render title if necessary - if (*PRENDERTITLES) { - CBox rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + offset.x, ASSIGNEDBOX.y - pMonitor->vecPosition.y + offset.y + BAR_PADDING_OUTER_VERT, m_fBarWidth, - ASSIGNEDBOX.h - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT * 2}; - rect.scale(pMonitor->scale); + rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + offset.x, ASSIGNEDBOX.y - pMonitor->vecPosition.y + offset.y + BAR_PADDING_OUTER_VERT, m_fBarWidth, + ASSIGNEDBOX.h - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT * 2}; + rect.scale(pMonitor->scale); + if (*PGRADIENTS) { + const auto& GRADIENTTEX = (m_dwGroupMembers[i] == g_pCompositor->m_pLastWindow ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) : + (GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive)); + if (GRADIENTTEX.m_iTexID != 0) + g_pHyprOpenGL->renderTexture(GRADIENTTEX, &rect, 1.0); + } + + if (*PRENDERTITLES) { CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[i]->m_szTitle); if (!pTitleTex) @@ -142,12 +150,6 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale})) .get(); - const auto& GRADIENTTEX = (m_dwGroupMembers[i] == g_pCompositor->m_pLastWindow ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) : - (GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive)); - - if (*PGRADIENTS && GRADIENTTEX.m_iTexID != 0) - g_pHyprOpenGL->renderTexture(GRADIENTTEX, &rect, 1.0); - rect.y += (ASSIGNEDBOX.h / 2.0 - (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) / 2.0) * pMonitor->scale; rect.height = (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale; @@ -243,7 +245,7 @@ CTitleTex::~CTitleTex() { tex.destroyTexture(); } -void renderGradientTo(CTexture& tex, const CColor& grad) { +void renderGradientTo(CTexture& tex, CGradientValueData* grad) { if (!g_pCompositor->m_pLastMonitor) return; @@ -261,8 +263,12 @@ void renderGradientTo(CTexture& tex, const CColor& grad) { cairo_pattern_t* pattern; pattern = cairo_pattern_create_linear(0, 0, 0, bufferSize.y); - cairo_pattern_add_color_stop_rgba(pattern, 1, grad.r, grad.g, grad.b, grad.a); - cairo_pattern_add_color_stop_rgba(pattern, 0, grad.r, grad.g, grad.b, 0); + + for (unsigned long i = 0; i < grad->m_vColors.size(); i++) { + cairo_pattern_add_color_stop_rgba(pattern, 1 - (double)(i + 1) / (grad->m_vColors.size() + 1), grad->m_vColors[i].r, grad->m_vColors[i].g, grad->m_vColors[i].b, + grad->m_vColors[i].a); + } + cairo_rectangle(CAIRO, 0, 0, bufferSize.x, bufferSize.y); cairo_set_source(CAIRO, pattern); cairo_fill(CAIRO); @@ -290,13 +296,13 @@ void renderGradientTo(CTexture& tex, const CColor& grad) { } void refreshGroupBarGradients() { - static auto* const PGRADIENTS = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue; - static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:gradients")->intValue; + static auto* const PGRADIENTS = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue; + static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:gradients")->intValue; - static auto* const PGROUPCOLACTIVE = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.active")->data; - static auto* const PGROUPCOLINACTIVE = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.inactive")->data; - static auto* const PGROUPCOLACTIVELOCKED = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_active")->data; - static auto* const PGROUPCOLINACTIVELOCKED = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_inactive")->data; + CGradientValueData* PGROUPCOLACTIVE = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.active")->data.get(); + CGradientValueData* PGROUPCOLINACTIVE = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.inactive")->data.get(); + CGradientValueData* PGROUPCOLACTIVELOCKED = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_active")->data.get(); + CGradientValueData* PGROUPCOLINACTIVELOCKED = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_inactive")->data.get(); g_pHyprRenderer->makeEGLCurrent(); @@ -310,10 +316,10 @@ void refreshGroupBarGradients() { if (!*PENABLED || !*PGRADIENTS) return; - renderGradientTo(m_tGradientActive, ((CGradientValueData*)PGROUPCOLACTIVE->get())->m_vColors[0]); - renderGradientTo(m_tGradientInactive, ((CGradientValueData*)PGROUPCOLINACTIVE->get())->m_vColors[0]); - renderGradientTo(m_tGradientLockedActive, ((CGradientValueData*)PGROUPCOLACTIVELOCKED->get())->m_vColors[0]); - renderGradientTo(m_tGradientLockedInactive, ((CGradientValueData*)PGROUPCOLINACTIVELOCKED->get())->m_vColors[0]); + renderGradientTo(m_tGradientActive, PGROUPCOLACTIVE); + renderGradientTo(m_tGradientInactive, PGROUPCOLINACTIVE); + renderGradientTo(m_tGradientLockedActive, PGROUPCOLACTIVELOCKED); + renderGradientTo(m_tGradientLockedInactive, PGROUPCOLINACTIVELOCKED); } bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) { diff --git a/subprojects/packagefiles/wlroots-meson-build.patch b/subprojects/packagefiles/wlroots-meson-build.patch deleted file mode 100644 index a184eb77..00000000 --- a/subprojects/packagefiles/wlroots-meson-build.patch +++ /dev/null @@ -1,45 +0,0 @@ -diff --git a/include/meson.build b/include/meson.build -index e669800..687786b 100644 ---- a/include/meson.build -+++ b/include/meson.build -@@ -1,4 +1,5 @@ --subdir('wlr') -+run_command('ln', '-sf', join_paths(meson.project_source_root(), 'include', 'wlr'), join_paths(meson.project_source_root(), 'include', 'wlroots'), check: true) -+subdir('wlroots') - - exclude_files = ['meson.build', 'config.h.in', 'version.h.in'] - if not features.get('drm-backend') -@@ -24,8 +25,8 @@ if not features.get('session') - exclude_files += 'backend/session.h' - endif - --install_subdir('wlr', -- install_dir: get_option('includedir'), -+install_subdir('wlroots', -+ install_dir: join_paths(get_option('includedir'), 'hyprland'), - exclude_files: exclude_files, - ) - -diff --git a/include/wlr/meson.build b/include/wlr/meson.build -index f7ca413..0a86d54 100644 ---- a/include/wlr/meson.build -+++ b/include/wlr/meson.build -@@ -22,4 +22,4 @@ ver_h = configure_file( - configuration: version_data, - ) - --install_headers(conf_h, ver_h, subdir: 'wlr') -+install_headers(conf_h, ver_h, subdir: join_paths('hyprland', 'wlroots')) -diff --git a/meson.build b/meson.build -index 29b103a..0b6e5a4 100644 ---- a/meson.build -+++ b/meson.build -@@ -15,7 +15,7 @@ project( - version_major = version.split('.')[0] - version_minor = version.split('.')[1] - assert(version_major == '0') --soversion = version_minor.to_int() - 5 -+soversion = 13032 - - little_endian = target_machine.endian() == 'little' - big_endian = target_machine.endian() == 'big' diff --git a/subprojects/wlroots b/subprojects/wlroots index f81c3d93..00b869c1 160000 --- a/subprojects/wlroots +++ b/subprojects/wlroots @@ -1 +1 @@ -Subproject commit f81c3d93cd6f61b20ae784297679283438def8df +Subproject commit 00b869c1a96f300a8f25da95d624524895e0ddf2 diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap deleted file mode 100644 index 915385df..00000000 --- a/subprojects/wlroots.wrap +++ /dev/null @@ -1,7 +0,0 @@ -[wrap-git] -directory = wlroots -url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = f81c3d93cd6f61b20ae784297679283438def8df -depth = 1 - -diff_files = wlroots-meson-build.patch