diff --git a/.github/workflows/new-pr-comment.yml b/.github/workflows/new-pr-comment.yml index be017db81..36ea19098 100644 --- a/.github/workflows/new-pr-comment.yml +++ b/.github/workflows/new-pr-comment.yml @@ -7,6 +7,13 @@ on: jobs: comment: + if: > + github.event.pull_request.user.login != 'vaxerski' && + github.event.pull_request.user.login != 'fufexan' && + github.event.pull_request.user.login != 'gulafaran' && + github.event.pull_request.user.login != 'ujint34' && + github.event.pull_request.user.login != 'paideiadilemma' && + github.event.pull_request.user.login != 'notashelf' runs-on: ubuntu-latest permissions: pull-requests: write diff --git a/CMakeLists.txt b/CMakeLists.txt index 07a33cb6d..a192a6942 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,7 +128,7 @@ find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION}) set(AQUAMARINE_MINIMUM_VERSION 0.9.3) set(HYPRLANG_MINIMUM_VERSION 0.6.7) set(HYPRCURSOR_MINIMUM_VERSION 0.1.7) -set(HYPRUTILS_MINIMUM_VERSION 0.10.2) +set(HYPRUTILS_MINIMUM_VERSION 0.11.0) set(HYPRGRAPHICS_MINIMUM_VERSION 0.1.6) pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=${AQUAMARINE_MINIMUM_VERSION}) diff --git a/hyprpm/src/core/DataState.cpp b/hyprpm/src/core/DataState.cpp index 42f1d4289..64f3cfa02 100644 --- a/hyprpm/src/core/DataState.cpp +++ b/hyprpm/src/core/DataState.cpp @@ -93,6 +93,7 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) { auto DATA = toml::table{ {"repository", toml::table{ {"name", repo.name}, + {"author", repo.author}, {"hash", repo.hash}, {"url", repo.url}, {"rev", repo.rev} @@ -122,31 +123,32 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) { Debug::die("{}", failureString("Failed to write plugin state")); } -bool DataState::pluginRepoExists(const std::string& urlOrName) { +bool DataState::pluginRepoExists(const SPluginRepoIdentifier identifier) { ensureStateStoreExists(); for (const auto& stateFile : getPluginStates()) { - const auto STATE = toml::parse_file(stateFile.c_str()); - const auto NAME = STATE["repository"]["name"].value_or(""); - const auto URL = STATE["repository"]["url"].value_or(""); + const auto STATE = toml::parse_file(stateFile.c_str()); + const auto NAME = STATE["repository"]["name"].value_or(""); + const auto AUTHOR = STATE["repository"]["author"].value_or(""); + const auto URL = STATE["repository"]["url"].value_or(""); - if (URL == urlOrName || NAME == urlOrName) + if (identifier.matches(URL, NAME, AUTHOR)) return true; } return false; } -void DataState::removePluginRepo(const std::string& urlOrName) { +void DataState::removePluginRepo(const SPluginRepoIdentifier identifier) { ensureStateStoreExists(); for (const auto& stateFile : getPluginStates()) { - const auto STATE = toml::parse_file(stateFile.c_str()); - const auto NAME = STATE["repository"]["name"].value_or(""); - const auto URL = STATE["repository"]["url"].value_or(""); - - if (URL == urlOrName || NAME == urlOrName) { + const auto STATE = toml::parse_file(stateFile.c_str()); + const auto NAME = STATE["repository"]["name"].value_or(""); + const auto AUTHOR = STATE["repository"]["author"].value_or(""); + const auto URL = STATE["repository"]["url"].value_or(""); + if (identifier.matches(URL, NAME, AUTHOR)) { // unload the plugins!! for (const auto& file : std::filesystem::directory_iterator(stateFile.parent_path())) { if (!file.path().string().ends_with(".so")) @@ -219,16 +221,18 @@ std::vector DataState::getAllRepositories() { for (const auto& stateFile : getPluginStates()) { const auto STATE = toml::parse_file(stateFile.c_str()); - const auto NAME = STATE["repository"]["name"].value_or(""); - const auto URL = STATE["repository"]["url"].value_or(""); - const auto REV = STATE["repository"]["rev"].value_or(""); - const auto HASH = STATE["repository"]["hash"].value_or(""); + const auto NAME = STATE["repository"]["name"].value_or(""); + const auto AUTHOR = STATE["repository"]["author"].value_or(""); + const auto URL = STATE["repository"]["url"].value_or(""); + const auto REV = STATE["repository"]["rev"].value_or(""); + const auto HASH = STATE["repository"]["hash"].value_or(""); SPluginRepository repo; - repo.hash = HASH; - repo.name = NAME; - repo.url = URL; - repo.rev = REV; + repo.hash = HASH; + repo.name = NAME; + repo.author = AUTHOR; + repo.url = URL; + repo.rev = REV; for (const auto& [key, val] : STATE) { if (key == "repository") @@ -247,7 +251,7 @@ std::vector DataState::getAllRepositories() { return repos; } -bool DataState::setPluginEnabled(const std::string& name, bool enabled) { +bool DataState::setPluginEnabled(const SPluginRepoIdentifier identifier, bool enabled) { ensureStateStoreExists(); for (const auto& stateFile : getPluginStates()) { @@ -256,8 +260,17 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) { if (key == "repository") continue; - if (key.str() != name) - continue; + switch (identifier.type) { + case IDENTIFIER_NAME: + if (key.str() != identifier.name) + continue; + break; + case IDENTIFIER_AUTHOR_NAME: + if (STATE["repository"]["author"] != identifier.author || key.str() != identifier.name) + continue; + break; + default: return false; + } const auto FAILED = STATE[key]["failed"].value_or(false); diff --git a/hyprpm/src/core/DataState.hpp b/hyprpm/src/core/DataState.hpp index dfab535a7..d9872b907 100644 --- a/hyprpm/src/core/DataState.hpp +++ b/hyprpm/src/core/DataState.hpp @@ -15,11 +15,11 @@ namespace DataState { std::vector getPluginStates(); void ensureStateStoreExists(); void addNewPluginRepo(const SPluginRepository& repo); - void removePluginRepo(const std::string& urlOrName); - bool pluginRepoExists(const std::string& urlOrName); + void removePluginRepo(const SPluginRepoIdentifier identifier); + bool pluginRepoExists(const SPluginRepoIdentifier identifier); void updateGlobalState(const SGlobalState& state); void purgeAllCache(); SGlobalState getGlobalState(); - bool setPluginEnabled(const std::string& name, bool enabled); + bool setPluginEnabled(const SPluginRepoIdentifier identifier, bool enabled); std::vector getAllRepositories(); -}; \ No newline at end of file +}; diff --git a/hyprpm/src/core/Plugin.cpp b/hyprpm/src/core/Plugin.cpp new file mode 100644 index 000000000..3aaa85925 --- /dev/null +++ b/hyprpm/src/core/Plugin.cpp @@ -0,0 +1,48 @@ +#include "Plugin.hpp" + +SPluginRepoIdentifier SPluginRepoIdentifier::fromUrl(const std::string& url) { + return SPluginRepoIdentifier{.type = IDENTIFIER_URL, .url = url}; +} + +SPluginRepoIdentifier SPluginRepoIdentifier::fromName(const std::string& name) { + return SPluginRepoIdentifier{.type = IDENTIFIER_NAME, .name = name}; +} + +SPluginRepoIdentifier SPluginRepoIdentifier::fromAuthorName(const std::string& author, const std::string& name) { + return SPluginRepoIdentifier{.type = IDENTIFIER_AUTHOR_NAME, .name = name, .author = author}; +} + +SPluginRepoIdentifier SPluginRepoIdentifier::fromString(const std::string& string) { + if (string.find(':') != std::string::npos) { + return SPluginRepoIdentifier{.type = IDENTIFIER_URL, .url = string}; + } else { + auto slashPos = string.find('/'); + if (slashPos != std::string::npos) { + std::string author = string.substr(0, slashPos); + std::string name = string.substr(slashPos + 1, string.size() - slashPos - 1); + return SPluginRepoIdentifier{.type = IDENTIFIER_AUTHOR_NAME, .name = name, .author = author}; + } else { + return SPluginRepoIdentifier{.type = IDENTIFIER_NAME, .name = string}; + } + } +} + +std::string SPluginRepoIdentifier::toString() const { + switch (type) { + case IDENTIFIER_NAME: return name; + case IDENTIFIER_AUTHOR_NAME: return author + '/' + name; + case IDENTIFIER_URL: return url; + } + + return ""; +} + +bool SPluginRepoIdentifier::matches(const std::string& url, const std::string& name, const std::string& author) const { + switch (type) { + case IDENTIFIER_URL: return this->url == url; + case IDENTIFIER_NAME: return this->name == name; + case IDENTIFIER_AUTHOR_NAME: return this->author == author && this->name == name; + } + + return false; +} diff --git a/hyprpm/src/core/Plugin.hpp b/hyprpm/src/core/Plugin.hpp index e66031c98..a8c740848 100644 --- a/hyprpm/src/core/Plugin.hpp +++ b/hyprpm/src/core/Plugin.hpp @@ -14,6 +14,27 @@ struct SPluginRepository { std::string url; std::string rev; std::string name; + std::string author; std::vector plugins; std::string hash; -}; \ No newline at end of file +}; + +enum ePluginRepoIdentifierType { + IDENTIFIER_URL, + IDENTIFIER_NAME, + IDENTIFIER_AUTHOR_NAME +}; + +struct SPluginRepoIdentifier { + ePluginRepoIdentifierType type; + std::string url = ""; + std::string name = ""; + std::string author = ""; + + static SPluginRepoIdentifier fromString(const std::string& string); + static SPluginRepoIdentifier fromUrl(const std::string& Url); + static SPluginRepoIdentifier fromName(const std::string& name); + static SPluginRepoIdentifier fromAuthorName(const std::string& author, const std::string& name); + std::string toString() const; + bool matches(const std::string& url, const std::string& name, const std::string& author) const; +}; diff --git a/hyprpm/src/core/PluginManager.cpp b/hyprpm/src/core/PluginManager.cpp index ed952eecf..0d35b4ae0 100644 --- a/hyprpm/src/core/PluginManager.cpp +++ b/hyprpm/src/core/PluginManager.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -136,7 +137,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& return false; } - if (DataState::pluginRepoExists(url)) { + if (DataState::pluginRepoExists(SPluginRepoIdentifier::fromUrl(url))) { std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Repository already installed.")); return false; } @@ -333,10 +334,13 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD"); if (repohash.length() > 0) repohash.pop_back(); - repo.name = pManifest->m_repository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_repository.name; - repo.url = url; - repo.rev = rev; - repo.hash = repohash; + auto lastSlash = url.find_last_of('/'); + auto secondLastSlash = url.find_last_of('/', lastSlash - 1); + repo.name = pManifest->m_repository.name.empty() ? url.substr(lastSlash + 1) : pManifest->m_repository.name; + repo.author = url.substr(secondLastSlash + 1, lastSlash - secondLastSlash - 1); + repo.url = url; + repo.rev = rev; + repo.hash = repohash; for (auto const& p : pManifest->m_plugins) { repo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, false, p.failed}); } @@ -356,13 +360,13 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& return true; } -bool CPluginManager::removePluginRepo(const std::string& urlOrName) { - if (!DataState::pluginRepoExists(urlOrName)) { +bool CPluginManager::removePluginRepo(const SPluginRepoIdentifier identifier) { + if (!DataState::pluginRepoExists(identifier)) { std::println(stderr, "\n{}", failureString("Could not remove the repository. Repository is not installed.")); return false; } - std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << urlOrName << "\n " + std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << identifier.toString() << "\n " << "Are you sure? [Y/n] "; std::fflush(stdout); std::string input; @@ -373,7 +377,7 @@ bool CPluginManager::removePluginRepo(const std::string& urlOrName) { return false; } - DataState::removePluginRepo(urlOrName); + DataState::removePluginRepo(identifier); return true; } @@ -444,7 +448,6 @@ eHeadersErrors CPluginManager::headersValid() { } bool CPluginManager::updateHeaders(bool force) { - DataState::ensureStateStoreExists(); const auto HLVER = getHyprlandVersion(false); @@ -772,7 +775,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { const auto OLDPLUGINIT = std::find_if(repo.plugins.begin(), repo.plugins.end(), [&](const auto& other) { return other.name == p.name; }); newrepo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false}); } - DataState::removePluginRepo(newrepo.name); + DataState::removePluginRepo(SPluginRepoIdentifier::fromName(newrepo.name)); DataState::addNewPluginRepo(newrepo); std::filesystem::remove_all(m_szWorkingPluginDirectory); @@ -797,17 +800,23 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { return true; } -bool CPluginManager::enablePlugin(const std::string& name) { - bool ret = DataState::setPluginEnabled(name, true); +bool CPluginManager::enablePlugin(const SPluginRepoIdentifier identifier) { + bool ret = false; + + switch (identifier.type) { + case IDENTIFIER_NAME: + case IDENTIFIER_AUTHOR_NAME: ret = DataState::setPluginEnabled(identifier, true); break; + default: return false; + } if (ret) - std::println("{}", successString("Enabled {}", name)); + std::println("{}", successString("Enabled {}", identifier.name)); return ret; } -bool CPluginManager::disablePlugin(const std::string& name) { - bool ret = DataState::setPluginEnabled(name, false); +bool CPluginManager::disablePlugin(const SPluginRepoIdentifier identifier) { + bool ret = DataState::setPluginEnabled(identifier, false); if (ret) - std::println("{}", successString("Disabled {}", name)); + std::println("{}", successString("Disabled {}", identifier.name)); return ret; } @@ -928,7 +937,7 @@ void CPluginManager::listAllPlugins() { const auto REPOS = DataState::getAllRepositories(); for (auto const& r : REPOS) { - std::println("{}", infoString("Repository {}:", r.name)); + std::println("{}", infoString("Repository {} (by {}):", r.name, r.author)); for (auto const& p : r.plugins) { std::println(" │ Plugin {}", p.name); diff --git a/hyprpm/src/core/PluginManager.hpp b/hyprpm/src/core/PluginManager.hpp index 2425f5ec9..10a71469d 100644 --- a/hyprpm/src/core/PluginManager.hpp +++ b/hyprpm/src/core/PluginManager.hpp @@ -2,8 +2,10 @@ #include #include +#include #include #include +#include "Plugin.hpp" enum eHeadersErrors { HEADERS_OK = 0, @@ -46,7 +48,7 @@ class CPluginManager { CPluginManager(); bool addNewPluginRepo(const std::string& url, const std::string& rev); - bool removePluginRepo(const std::string& urlOrName); + bool removePluginRepo(const SPluginRepoIdentifier identifier); eHeadersErrors headersValid(); bool updateHeaders(bool force = false); @@ -54,8 +56,8 @@ class CPluginManager { void listAllPlugins(); - bool enablePlugin(const std::string& name); - bool disablePlugin(const std::string& name); + bool enablePlugin(const SPluginRepoIdentifier identifier); + bool disablePlugin(const SPluginRepoIdentifier identifier); ePluginLoadStateReturn ensurePluginsLoadState(bool forceReload = false); bool loadUnloadPlugin(const std::string& path, bool load); diff --git a/hyprpm/src/main.cpp b/hyprpm/src/main.cpp index 817049ff5..dced58e75 100644 --- a/hyprpm/src/main.cpp +++ b/hyprpm/src/main.cpp @@ -13,25 +13,25 @@ using namespace Hyprutils::Utils; constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager ┃ -┣ add [url] [git rev] → Install a new plugin repository from git. Git revision -┃ is optional, when set, commit locks are ignored. -┣ remove [url/name] → Remove an installed plugin repository. -┣ enable [name] → Enable a plugin. -┣ disable [name] → Disable a plugin. -┣ update → Check and update all plugins if needed. -┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded. -┣ list → List all installed plugins. -┣ purge-cache → Remove the entire hyprpm cache, built plugins, hyprpm settings and headers. +┣ add [git rev] → Install a new plugin repository from git. Git revision +┃ is optional, when set, commit locks are ignored. +┣ remove → Remove an installed plugin repository. +┣ enable → Enable a plugin. +┣ disable → Disable a plugin. +┣ update → Check and update all plugins if needed. +┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded. +┣ list → List all installed plugins. +┣ purge-cache → Remove the entire hyprpm cache, built plugins, hyprpm settings and headers. ┃ ┣ Flags: ┃ -┣ --notify | -n → Send a hyprland notification confirming successful plugin load. -┃ Warnings/Errors trigger notifications regardless of this flag. -┣ --help | -h → Show this menu. -┣ --verbose | -v → Enable too much logging. -┣ --force | -f → Force an operation ignoring checks (e.g. update -f). -┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources. -┣ --hl-url | → Pass a custom hyprland source url. +┣ --notify | -n → Send a hyprland notification confirming successful plugin load. +┃ Warnings/Errors trigger notifications regardless of this flag. +┣ --help | -h → Show this menu. +┣ --verbose | -v → Enable too much logging. +┣ --force | -f → Force an operation ignoring checks (e.g. update -f). +┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources. +┣ --hl-url | → Pass a custom hyprland source url. ┗ )#"; @@ -126,7 +126,7 @@ int main(int argc, char** argv, char** envp) { NSys::root::cacheSudo(); CScopeGuard x([] { NSys::root::dropSudo(); }); - return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1; + return g_pPluginManager->removePluginRepo(SPluginRepoIdentifier::fromString(command[1])) ? 0 : 1; } else if (command[0] == "update") { NSys::root::cacheSudo(); CScopeGuard x([] { NSys::root::dropSudo(); }); @@ -160,7 +160,7 @@ int main(int argc, char** argv, char** envp) { return 1; } - if (!g_pPluginManager->enablePlugin(command[1])) { + if (!g_pPluginManager->enablePlugin(SPluginRepoIdentifier::fromString(command[1]))) { std::println(stderr, "{}", failureString("Couldn't enable plugin (missing?)")); return 1; } @@ -181,7 +181,7 @@ int main(int argc, char** argv, char** envp) { return 1; } - if (!g_pPluginManager->disablePlugin(command[1])) { + if (!g_pPluginManager->disablePlugin(SPluginRepoIdentifier::fromString(command[1]))) { std::println(stderr, "{}", failureString("Couldn't disable plugin (missing?)")); return 1; } diff --git a/nix/default.nix b/nix/default.nix index 1531a7a28..27ecdf604 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -11,7 +11,7 @@ cairo, epoll-shim, git, - glaze, + glaze-hyprland, gtest, hyprcursor, hyprgraphics, @@ -143,7 +143,7 @@ in aquamarine cairo git - glaze + glaze-hyprland gtest hyprcursor hyprgraphics diff --git a/nix/overlays.nix b/nix/overlays.nix index 9d855e77d..fdb3e6528 100644 --- a/nix/overlays.nix +++ b/nix/overlays.nix @@ -30,6 +30,7 @@ in { inputs.hyprwayland-scanner.overlays.default inputs.hyprwire.overlays.default self.overlays.udis86 + self.overlays.glaze # Hyprland packages themselves (final: _prev: let @@ -110,4 +111,13 @@ in { patches = []; }); }; + + # Even though glaze itself disables it by default, nixpkgs sets ENABLE_SSL set to true. + # Since we don't include openssl, the build failes without the `enableSSL = false;` override + glaze = final: prev: { + glaze-hyprland = prev.glaze.override { + enableSSL = false; + enableInterop = false; + }; + }; } diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 0d9c2f8c8..3c67979f7 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -894,7 +894,10 @@ bool CCompositor::monitorExists(PHLMONITOR pMonitor) { } PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t properties, PHLWINDOW pIgnoreWindow) { - const auto PMONITOR = getMonitorFromVector(pos); + const auto PMONITOR = getMonitorFromVector(pos); + if (!PMONITOR) + return nullptr; + static auto PRESIZEONBORDER = CConfigValue("general:resize_on_border"); static auto PBORDERSIZE = CConfigValue("general:border_size"); static auto PBORDERGRABEXTEND = CConfigValue("general:extend_border_grab_area"); @@ -993,8 +996,18 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper const WORKSPACEID WSPID = special ? PMONITOR->activeSpecialWorkspaceID() : PMONITOR->activeWorkspaceID(); const auto PWORKSPACE = getWorkspaceByID(WSPID); - if (PWORKSPACE->m_hasFullscreenWindow && !(properties & Desktop::View::SKIP_FULLSCREEN_PRIORITY) && !ONLY_PRIORITY) - return PWORKSPACE->getFullscreenWindow(); + if (PWORKSPACE->m_hasFullscreenWindow && !(properties & Desktop::View::SKIP_FULLSCREEN_PRIORITY) && !ONLY_PRIORITY) { + const auto FS_WINDOW = PWORKSPACE->getFullscreenWindow(); + + if (!FS_WINDOW) + return nullptr; + + // for maximized windows, don't return a window if we are not directly on it. + if (FS_WINDOW->m_fullscreenState.internal != FSMODE_MAXIMIZED || FS_WINDOW->getWindowBoxUnified(properties).containsPoint(pos)) + return PWORKSPACE->getFullscreenWindow(); + else + return nullptr; + } auto found = floating(false); if (found) @@ -1130,7 +1143,7 @@ PHLMONITOR CCompositor::getRealMonitorFromOutput(SP out) { SP CCompositor::vectorToLayerPopupSurface(const Vector2D& pos, PHLMONITOR monitor, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound) { for (auto const& lsl : monitor->m_layerSurfaceLayers | std::views::reverse) { for (auto const& ls : lsl | std::views::reverse) { - if (!ls->visible() || ls->m_fadingOut) + if (!ls->aliveAndVisible()) continue; auto SURFACEAT = ls->m_popupHead->at(pos, true); @@ -1150,7 +1163,7 @@ SP CCompositor::vectorToLayerSurface(const Vector2D& pos, st bool aboveLockscreen) { for (auto const& ls : *layerSurfaces | std::views::reverse) { - if (!ls->visible() || ls->m_fadingOut || (aboveLockscreen && ls->m_ruleApplicator->aboveLock().valueOrDefault() != 2)) + if (!ls->aliveAndVisible() || (aboveLockscreen && ls->m_ruleApplicator->aboveLock().valueOrDefault() != 2)) continue; auto [surf, local] = ls->m_layerSurface->m_surface->at(pos - ls->m_geometry.pos(), true); @@ -1380,6 +1393,9 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) { const auto WINDOWIDEALBB = pWindow->isFullscreen() ? CBox{PMONITOR->m_position, PMONITOR->m_size} : pWindow->getWindowIdealBoundingBoxIgnoreReserved(); const auto PWORKSPACE = pWindow->m_workspace; + if (!PWORKSPACE) + return nullptr; // ?? + return getWindowInDirection(WINDOWIDEALBB, PWORKSPACE, dir, pWindow, pWindow->m_isFloating); } @@ -1621,12 +1637,12 @@ bool CCompositor::isPointOnReservedArea(const Vector2D& point, const PHLMONITOR const auto PMONITOR = pMonitor ? pMonitor : getMonitorFromVector(point); auto box = PMONITOR->logicalBox(); - if (VECNOTINRECT(point, box.x - 1, box.y - 1, box.w + 2, box.h + 2)) + if (VECNOTINRECT(point, box.x - 1, box.y - 1, box.x + box.w + 1, box.y + box.h + 1)) return false; PMONITOR->m_reservedArea.applyip(box); - return VECNOTINRECT(point, box.x, box.y, box.x, box.y); + return VECNOTINRECT(point, box.x, box.y, box.x + box.w, box.y + box.h); } CBox CCompositor::calculateX11WorkArea() { @@ -2347,7 +2363,7 @@ PHLLS CCompositor::getLayerSurfaceFromSurface(SP pSurface) { std::pair, bool> result = {pSurface, false}; for (auto const& ls : m_layers) { - if (!ls->visible() || ls->m_fadingOut) + if (!ls->aliveAndVisible()) continue; if (ls->m_layerSurface->m_surface == pSurface) diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp index 85655dfd8..38bb0a20c 100644 --- a/src/config/ConfigDescriptions.hpp +++ b/src/config/ConfigDescriptions.hpp @@ -1658,6 +1658,12 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{false}, }, + SConfigOptionDescription{ + .value = "cursor:zoom_detached_camera", + .description = "Detaches the camera from the mouse when zoomed in, only ever moving to keep the mouse in view", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{true}, + }, SConfigOptionDescription{ .value = "cursor:enable_hyprcursor", .description = "whether to enable hyprcursor support", diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index f8acb4738..94147f499 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -734,6 +734,7 @@ CConfigManager::CConfigManager() { registerConfigVar("cursor:zoom_factor", {1.f}); registerConfigVar("cursor:zoom_rigid", Hyprlang::INT{0}); registerConfigVar("cursor:zoom_disable_aa", Hyprlang::INT{0}); + registerConfigVar("cursor:zoom_detached_camera", Hyprlang::INT{1}); registerConfigVar("cursor:enable_hyprcursor", Hyprlang::INT{1}); registerConfigVar("cursor:sync_gsettings_theme", Hyprlang::INT{1}); registerConfigVar("cursor:hide_on_key_press", Hyprlang::INT{0}); diff --git a/src/desktop/view/GlobalViewMethods.cpp b/src/desktop/view/GlobalViewMethods.cpp index 97dc99606..83513e816 100644 --- a/src/desktop/view/GlobalViewMethods.cpp +++ b/src/desktop/view/GlobalViewMethods.cpp @@ -18,7 +18,7 @@ std::vector> View::getViewsForWorkspace(PHLWORKSPACE ws) { std::vector> views; for (const auto& w : g_pCompositor->m_windows) { - if (!w->visible() || w->m_workspace != ws) + if (!w->aliveAndVisible() || w->m_workspace != ws) continue; views.emplace_back(w); @@ -38,7 +38,7 @@ std::vector> View::getViewsForWorkspace(PHLWORKSPACE ws) { w->m_popupHead->breadthfirst( [&views](SP s, void* data) { auto surf = s->wlSurface(); - if (!surf || !s->visible()) + if (!surf || !s->aliveAndVisible()) return; views.emplace_back(surf->view()); @@ -48,7 +48,7 @@ std::vector> View::getViewsForWorkspace(PHLWORKSPACE ws) { } for (const auto& l : g_pCompositor->m_layers) { - if (!l->visible() || l->m_monitor != ws->m_monitor) + if (!l->aliveAndVisible() || l->m_monitor != ws->m_monitor) continue; views.emplace_back(l); @@ -56,7 +56,7 @@ std::vector> View::getViewsForWorkspace(PHLWORKSPACE ws) { l->m_popupHead->breadthfirst( [&views](SP p, void* data) { auto surf = p->wlSurface(); - if (!surf || !p->visible()) + if (!surf || !p->aliveAndVisible()) return; views.emplace_back(surf->view()); @@ -65,7 +65,7 @@ std::vector> View::getViewsForWorkspace(PHLWORKSPACE ws) { } for (const auto& v : g_pCompositor->m_otherViews) { - if (!v->visible() || !v->desktopComponent()) + if (!v->aliveAndVisible() || !v->desktopComponent()) continue; if (v->type() == VIEW_TYPE_LOCK_SCREEN) { diff --git a/src/desktop/view/LayerSurface.hpp b/src/desktop/view/LayerSurface.hpp index 3bca03d63..3660ee743 100644 --- a/src/desktop/view/LayerSurface.hpp +++ b/src/desktop/view/LayerSurface.hpp @@ -93,13 +93,13 @@ namespace Desktop::View { inline bool validMapped(PHLLS l) { if (!valid(l)) return false; - return l->visible(); + return l->aliveAndVisible(); } inline bool validMapped(PHLLSREF l) { if (!valid(l)) return false; - return l->visible(); + return l->aliveAndVisible(); } } diff --git a/src/desktop/view/View.cpp b/src/desktop/view/View.cpp index e7c6ce3ae..17a10c64b 100644 --- a/src/desktop/view/View.cpp +++ b/src/desktop/view/View.cpp @@ -1,4 +1,5 @@ #include "View.hpp" +#include "../../protocols/core/Compositor.hpp" using namespace Desktop; using namespace Desktop::View; @@ -14,3 +15,14 @@ IView::IView(SP pWlSurface) : m_wlSurface(pWlSurface) SP IView::resource() const { return m_wlSurface ? m_wlSurface->resource() : nullptr; } + +bool IView::aliveAndVisible() const { + auto res = resource(); + if (!res) + return false; + + if (!res->m_mapped) + return false; + + return visible(); +} diff --git a/src/desktop/view/View.hpp b/src/desktop/view/View.hpp index 0f412d2a1..4d777c36d 100644 --- a/src/desktop/view/View.hpp +++ b/src/desktop/view/View.hpp @@ -18,6 +18,7 @@ namespace Desktop::View { virtual SP wlSurface() const; virtual SP resource() const; + virtual bool aliveAndVisible() const; virtual eViewType type() const = 0; virtual bool visible() const = 0; virtual bool desktopComponent() const = 0; diff --git a/src/desktop/view/WLSurface.cpp b/src/desktop/view/WLSurface.cpp index 1bf90ae8d..8c3ce9db4 100644 --- a/src/desktop/view/WLSurface.cpp +++ b/src/desktop/view/WLSurface.cpp @@ -38,7 +38,7 @@ SP CWLSurface::resource() const { } bool CWLSurface::small() const { - if (!m_view || !m_view->visible() || m_view->type() != VIEW_TYPE_WINDOW || !exists()) + if (!m_view || !m_view->aliveAndVisible() || m_view->type() != VIEW_TYPE_WINDOW || !exists()) return false; if (!m_resource->m_current.texture) @@ -51,7 +51,7 @@ bool CWLSurface::small() const { } Vector2D CWLSurface::correctSmallVec() const { - if (!m_view || !m_view->visible() || m_view->type() != VIEW_TYPE_WINDOW || !exists() || !small() || !m_fillIgnoreSmall) + if (!m_view || !m_view->aliveAndVisible() || m_view->type() != VIEW_TYPE_WINDOW || !exists() || !small() || !m_fillIgnoreSmall) return {}; const auto SIZE = getViewporterCorrectedSize(); @@ -171,12 +171,6 @@ SP CWLSurface::constraint() const { return m_constraint.lock(); } -bool CWLSurface::visible() { - if (m_view) - return m_view->visible(); - return true; // non-desktop, we don't know much. -} - SP CWLSurface::fromResource(SP pSurface) { if (!pSurface) return nullptr; diff --git a/src/desktop/view/WLSurface.hpp b/src/desktop/view/WLSurface.hpp index 13c825941..944e863b7 100644 --- a/src/desktop/view/WLSurface.hpp +++ b/src/desktop/view/WLSurface.hpp @@ -38,7 +38,6 @@ namespace Desktop::View { Vector2D correctSmallVecBuf() const; // returns a corrective vector for small() surfaces, in BL coords Vector2D getViewporterCorrectedSize() const; CRegion computeDamage() const; // logical coordinates. May be wrong if the surface is unassigned - bool visible(); bool keyboardFocusable() const; SP view() const; diff --git a/src/desktop/view/Window.cpp b/src/desktop/view/Window.cpp index e27129a1f..2fc15566a 100644 --- a/src/desktop/view/Window.cpp +++ b/src/desktop/view/Window.cpp @@ -154,7 +154,7 @@ eViewType CWindow::type() const { } bool CWindow::visible() const { - return m_isMapped && !m_hidden && m_wlSurface && m_wlSurface->resource(); + return !m_hidden && ((m_isMapped && m_wlSurface && m_wlSurface->resource()) || (m_fadingOut && m_alpha->value() != 0.F)); } std::optional CWindow::logicalBox() const { @@ -639,14 +639,13 @@ void CWindow::onMap() { } void CWindow::onBorderAngleAnimEnd(WP pav) { - const auto PAV = pav.lock(); - if (!PAV) + if (!pav) return; - if (PAV->getStyle() != "loop" || !PAV->enabled()) + if (pav->getStyle() != "loop" || !pav->enabled()) return; - const auto PANIMVAR = dc*>(PAV.get()); + const auto PANIMVAR = dc*>(pav.get()); PANIMVAR->setCallbackOnEnd(nullptr); // we remove the callback here because otherwise setvalueandwarp will recurse this @@ -1197,14 +1196,19 @@ int CWindow::surfacesCount() { return no; } -void CWindow::clampWindowSize(const std::optional minSize, const std::optional maxSize) { +bool CWindow::clampWindowSize(const std::optional minSize, const std::optional maxSize) { const Vector2D REALSIZE = m_realSize->goal(); const Vector2D MAX = isFullscreen() ? Vector2D{INFINITY, INFINITY} : maxSize.value_or(Vector2D{INFINITY, INFINITY}); const Vector2D NEWSIZE = REALSIZE.clamp(minSize.value_or(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}), MAX); - const Vector2D DELTA = REALSIZE - NEWSIZE; + const bool changed = !(NEWSIZE == REALSIZE); - *m_realPosition = m_realPosition->goal() + DELTA / 2.0; - *m_realSize = NEWSIZE; + if (changed) { + const Vector2D DELTA = REALSIZE - NEWSIZE; + *m_realPosition = m_realPosition->goal() + DELTA / 2.0; + *m_realSize = NEWSIZE; + } + + return changed; } bool CWindow::isFullscreen() { @@ -1907,16 +1911,14 @@ std::optional CWindow::calculateExpression(const std::string& s) { } static void setVector2DAnimToMove(WP pav) { - const auto PAV = pav.lock(); - if (!PAV) + if (!pav) return; - CAnimatedVariable* animvar = dc*>(PAV.get()); + CAnimatedVariable* animvar = dc*>(pav.get()); animvar->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsMove")); - const auto PHLWINDOW = animvar->m_Context.pWindow.lock(); - if (PHLWINDOW) - PHLWINDOW->m_animatingIn = false; + if (animvar->m_Context.pWindow) + animvar->m_Context.pWindow->m_animatingIn = false; } void CWindow::mapWindow() { @@ -2554,8 +2556,8 @@ void CWindow::commitWindow() { const auto MINSIZE = m_xdgSurface->m_toplevel->layoutMinSize(); const auto MAXSIZE = m_xdgSurface->m_toplevel->layoutMaxSize(); - clampWindowSize(MINSIZE, MAXSIZE > Vector2D{1, 1} ? std::optional{MAXSIZE} : std::nullopt); - g_pHyprRenderer->damageWindow(m_self.lock()); + if (clampWindowSize(MINSIZE, MAXSIZE > Vector2D{1, 1} ? std::optional{MAXSIZE} : std::nullopt)) + g_pHyprRenderer->damageWindow(m_self.lock()); } if (!m_workspace->m_visible) diff --git a/src/desktop/view/Window.hpp b/src/desktop/view/Window.hpp index e1a42eda5..d5c86aac2 100644 --- a/src/desktop/view/Window.hpp +++ b/src/desktop/view/Window.hpp @@ -286,7 +286,7 @@ namespace Desktop::View { bool onSpecialWorkspace(); void activate(bool force = false); int surfacesCount(); - void clampWindowSize(const std::optional minSize, const std::optional maxSize); + bool clampWindowSize(const std::optional minSize, const std::optional maxSize); bool isFullscreen(); bool isEffectiveInternalFSMode(const eFullscreenMode) const; int getRealBorderSize() const; diff --git a/src/helpers/AnimatedVariable.hpp b/src/helpers/AnimatedVariable.hpp index e7d5fd8cd..f0bdc5a81 100644 --- a/src/helpers/AnimatedVariable.hpp +++ b/src/helpers/AnimatedVariable.hpp @@ -67,7 +67,7 @@ template using CAnimatedVariable = Hyprutils::Animation::CGenericAnimatedVariable; template -using PHLANIMVAR = SP>; +using PHLANIMVAR = UP>; template using PHLANIMVARREF = WP>; diff --git a/src/helpers/AsyncDialogBox.cpp b/src/helpers/AsyncDialogBox.cpp index 6257dcb0d..b36886807 100644 --- a/src/helpers/AsyncDialogBox.cpp +++ b/src/helpers/AsyncDialogBox.cpp @@ -147,6 +147,10 @@ bool CAsyncDialogBox::isRunning() const { return m_readEventSource; } +pid_t CAsyncDialogBox::getPID() const { + return m_dialogPid; +} + SP CAsyncDialogBox::lockSelf() { return m_selfWeakReference.lock(); } diff --git a/src/helpers/AsyncDialogBox.hpp b/src/helpers/AsyncDialogBox.hpp index 5f94be0da..8db516cea 100644 --- a/src/helpers/AsyncDialogBox.hpp +++ b/src/helpers/AsyncDialogBox.hpp @@ -26,6 +26,7 @@ class CAsyncDialogBox { SP> open(); void kill(); bool isRunning() const; + pid_t getPID() const; SP lockSelf(); @@ -51,4 +52,4 @@ class CAsyncDialogBox { // WARNING: cyclic reference. This will be removed once the event source is removed to avoid dangling pointers SP m_selfReference; WP m_selfWeakReference; -}; \ No newline at end of file +}; diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index fa76a982d..d01d2ac85 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1799,8 +1799,8 @@ uint16_t CMonitor::isDSBlocked(bool full) { return reasons; } - if (needsCM() && *PNONSHADER != CM_NS_IGNORE && !canNoShaderCM() && (!inHDR() || (PSURFACE->m_colorManagement.valid() && PSURFACE->m_colorManagement->isWindowsScRGB())) && - *PPASS != 1) + const bool surfaceIsHDR = PSURFACE->m_colorManagement.valid() && (PSURFACE->m_colorManagement->isHDR() || PSURFACE->m_colorManagement->isWindowsScRGB()); + if (needsCM() && *PNONSHADER != CM_NS_IGNORE && !canNoShaderCM() && ((inHDR() && (*PPASS == 0 || !surfaceIsHDR)) || (!inHDR() && (*PPASS != 1 || surfaceIsHDR)))) reasons |= DS_BLOCK_CM; return reasons; @@ -2065,10 +2065,13 @@ bool CMonitor::canNoShaderCM() { if (SRC_DESC->icc.fd >= 0 || m_imageDescription.icc.fd >= 0) return false; // no ICC support + static auto PSDREOTF = CConfigValue("render:cm_sdr_eotf"); // only primaries differ - if (SRC_DESC->transferFunction == m_imageDescription.transferFunction && SRC_DESC->transferFunctionPower == m_imageDescription.transferFunctionPower && - (!inHDR() || SRC_DESC->luminances == m_imageDescription.luminances) && SRC_DESC->masteringLuminances == m_imageDescription.masteringLuminances && - SRC_DESC->maxCLL == m_imageDescription.maxCLL && SRC_DESC->maxFALL == m_imageDescription.maxFALL) + if ((SRC_DESC->transferFunction == m_imageDescription.transferFunction || + (*PSDREOTF == 2 && SRC_DESC->transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB && + m_imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22)) && + SRC_DESC->transferFunctionPower == m_imageDescription.transferFunctionPower && (!inHDR() || SRC_DESC->luminances == m_imageDescription.luminances) && + SRC_DESC->masteringLuminances == m_imageDescription.masteringLuminances && SRC_DESC->maxCLL == m_imageDescription.maxCLL && SRC_DESC->maxFALL == m_imageDescription.maxFALL) return true; return false; diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index debf2ec74..95e5ce5cb 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -11,6 +11,7 @@ #include "CMType.hpp" #include +#include "MonitorZoomController.hpp" #include "time/Timer.hpp" #include "math/Math.hpp" #include "../desktop/reserved/ReservedArea.hpp" @@ -130,6 +131,8 @@ class CMonitor { uint32_t m_drmFormat = DRM_FORMAT_INVALID; uint32_t m_prevDrmFormat = DRM_FORMAT_INVALID; + CMonitorZoomController m_zoomController; + bool m_dpmsStatus = true; bool m_vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it. bool m_enabled10bit = false; // as above, this can be TRUE even if 10 bit failed. diff --git a/src/helpers/MonitorZoomController.cpp b/src/helpers/MonitorZoomController.cpp new file mode 100644 index 000000000..d90f416fe --- /dev/null +++ b/src/helpers/MonitorZoomController.cpp @@ -0,0 +1,97 @@ +#include "MonitorZoomController.hpp" + +#include +#include "../config/ConfigValue.hpp" +#include "../managers/input/InputManager.hpp" +#include "../render/OpenGL.hpp" +#include "desktop/DesktopTypes.hpp" +#include "render/Renderer.hpp" + +void CMonitorZoomController::zoomWithDetachedCamera(CBox& result, const SCurrentRenderData& m_renderData) { + const auto m = m_renderData.pMonitor; + auto monbox = CBox(0, 0, m->m_size.x, m->m_size.y); + const auto ZOOM = m_renderData.mouseZoomFactor; + const auto MOUSE = g_pInputManager->getMouseCoordsInternal() - m->m_position; + + if (m_lastZoomLevel != ZOOM) { + if (m_resetCameraState) { + m_resetCameraState = false; + m_camera = CBox(0, 0, m->m_size.x, m->m_size.y); + m_lastZoomLevel = 1.0f; + } + const CBox old = m_camera; + + // mouse normalized inside screen (0..1) + const float mx = MOUSE.x / m->m_size.x; + const float my = MOUSE.y / m->m_size.y; + // world-space point under the cursor before zoom + const float mouseWorldX = old.x + (mx * old.w); + const float mouseWorldY = old.y + (my * old.h); + + const auto CAMERAW = monbox.w / ZOOM; + const auto CAMERAH = monbox.h / ZOOM; + + // compute new top-left so the same world point stays under the cursor + const float newX = mouseWorldX - (mx * CAMERAW); + const float newY = mouseWorldY - (my * CAMERAH); + + m_camera = CBox(newX, newY, CAMERAW, CAMERAH); + // Detect if this zoom would've caused jerk to keep mouse in view and disable edges if so + if (!m_camera.copy().scaleFromCenter(.9).containsPoint(MOUSE)) + m_padCamEdges = false; + m_lastZoomLevel = ZOOM; + } + + // Keep mouse inside cameraview + auto smallerbox = m_camera; + // Prevent zoom step from causing us to jerk to keep mouse in padded camera view, + // but let us switch to the padded camera once the mouse moves into the safe area + if (!m_padCamEdges) + if (smallerbox.copy().scaleFromCenter(.9).containsPoint(MOUSE)) + m_padCamEdges = true; + if (m_padCamEdges) + smallerbox.scaleFromCenter(.9); + if (!smallerbox.containsPoint(MOUSE)) { + if (MOUSE.x < smallerbox.x) + m_camera.x -= smallerbox.x - MOUSE.x; + if (MOUSE.y < smallerbox.y) + m_camera.y -= smallerbox.y - MOUSE.y; + if (MOUSE.y > smallerbox.y + smallerbox.h) + m_camera.y += MOUSE.y - (smallerbox.y + smallerbox.h); + if (MOUSE.x > smallerbox.x + smallerbox.w) + m_camera.x += MOUSE.x - (smallerbox.x + smallerbox.w); + } + + auto z = ZOOM * m->m_scale; + monbox.scale(z).translate(-m_camera.pos() * z); + + result = monbox; +} + +void CMonitorZoomController::applyZoomTransform(CBox& monbox, const SCurrentRenderData& m_renderData) { + static auto PZOOMRIGID = CConfigValue("cursor:zoom_rigid"); + static auto PZOOMDETACHEDCAMERA = CConfigValue("cursor:zoom_detached_camera"); + const auto ZOOM = m_renderData.mouseZoomFactor; + + if (ZOOM == 1.0f) + return; + + const auto m = m_renderData.pMonitor; + const auto ORIGINAL = monbox; + const auto INITANIM = m->m_zoomAnimProgress->value() != 1.0; + + if (*PZOOMDETACHEDCAMERA && !INITANIM) + zoomWithDetachedCamera(monbox, m_renderData); + else { + const auto ZOOMCENTER = m_renderData.mouseZoomUseMouse ? (g_pInputManager->getMouseCoordsInternal() - m->m_position) * m->m_scale : m->m_transformedSize / 2.f; + + monbox.translate(-ZOOMCENTER).scale(ZOOM).translate(*PZOOMRIGID ? m->m_transformedSize / 2.0 : ZOOMCENTER); + } + + monbox.x = std::min(monbox.x, 0.0); + monbox.y = std::min(monbox.y, 0.0); + if (monbox.x + monbox.width < ORIGINAL.w) + monbox.x = ORIGINAL.w - monbox.width; + if (monbox.y + monbox.height < ORIGINAL.h) + monbox.y = ORIGINAL.h - monbox.height; +} diff --git a/src/helpers/MonitorZoomController.hpp b/src/helpers/MonitorZoomController.hpp new file mode 100644 index 000000000..4f7c9d7a2 --- /dev/null +++ b/src/helpers/MonitorZoomController.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "./math/Math.hpp" + +struct SCurrentRenderData; + +class CMonitorZoomController { + public: + bool m_resetCameraState = true; + + void applyZoomTransform(CBox& monbox, const SCurrentRenderData& m_renderData); + + private: + void zoomWithDetachedCamera(CBox& result, const SCurrentRenderData& m_renderData); + + CBox m_camera; + float m_lastZoomLevel = 1.0f; + bool m_padCamEdges = true; +}; diff --git a/src/managers/ANRManager.cpp b/src/managers/ANRManager.cpp index daab4d0a9..db7a245f5 100644 --- a/src/managers/ANRManager.cpp +++ b/src/managers/ANRManager.cpp @@ -1,4 +1,5 @@ #include "ANRManager.hpp" + #include "../helpers/fs/FsUtils.hpp" #include "../debug/Log.hpp" #include "../macros.hpp" @@ -29,6 +30,10 @@ CANRManager::CANRManager() { auto window = std::any_cast(data); for (const auto& d : m_data) { + // Window is ANR dialog + if (d->isRunning() && d->dialogBox->getPID() == window->getPID()) + return; + if (d->fitsWindow(window)) return; } @@ -84,7 +89,7 @@ void CANRManager::onTick() { if (data->missedResponses >= *PANRTHRESHOLD) { if (!data->isRunning() && !data->dialogSaidWait) { - data->runDialog(firstWindow->m_title, firstWindow->m_class, data->getPid()); + data->runDialog(firstWindow->m_title, firstWindow->m_class, data->getPID()); for (const auto& w : g_pCompositor->m_windows) { if (!w->m_isMapped) @@ -240,7 +245,7 @@ bool CANRManager::SANRData::isDefunct() const { return xdgBase.expired() && xwaylandSurface.expired(); } -pid_t CANRManager::SANRData::getPid() const { +pid_t CANRManager::SANRData::getPID() const { if (xdgBase) { pid_t pid = 0; wl_client_get_credentials(xdgBase->client(), &pid, nullptr, nullptr); diff --git a/src/managers/ANRManager.hpp b/src/managers/ANRManager.hpp index 286e834f9..3880249dc 100644 --- a/src/managers/ANRManager.hpp +++ b/src/managers/ANRManager.hpp @@ -44,7 +44,7 @@ class CANRManager { void killDialog(); bool isDefunct() const; bool fitsWindow(PHLWINDOW pWindow) const; - pid_t getPid() const; + pid_t getPID() const; void ping(); }; @@ -57,4 +57,4 @@ class CANRManager { std::vector> m_data; }; -inline UP g_pANRManager; \ No newline at end of file +inline UP g_pANRManager; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index bda1ff5a3..b33ca3c1e 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -1487,7 +1487,7 @@ SDispatchResult CKeybindManager::moveFocusTo(std::string args) { } const auto PLASTWINDOW = Desktop::focusState()->window(); - if (!PLASTWINDOW) { + if (!PLASTWINDOW || !PLASTWINDOW->aliveAndVisible()) { if (*PMONITORFALLBACK) tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg)); diff --git a/src/managers/animation/AnimationManager.cpp b/src/managers/animation/AnimationManager.cpp index a09f391b0..b4255a33c 100644 --- a/src/managers/animation/AnimationManager.cpp +++ b/src/managers/animation/AnimationManager.cpp @@ -209,8 +209,7 @@ void CHyprAnimationManager::tick() { static auto PANIMENABLED = CConfigValue("animations:enabled"); - for (size_t i = 0; i < m_vActiveAnimatedVariables.size(); i++) { - const auto PAV = m_vActiveAnimatedVariables[i].lock(); + for (const auto& PAV : m_vActiveAnimatedVariables) { if (!PAV) continue; diff --git a/src/managers/animation/AnimationManager.hpp b/src/managers/animation/AnimationManager.hpp index 2f411879b..b8acc53e7 100644 --- a/src/managers/animation/AnimationManager.hpp +++ b/src/managers/animation/AnimationManager.hpp @@ -22,13 +22,11 @@ class CHyprAnimationManager : public Hyprutils::Animation::CAnimationManager { template void createAnimation(const VarType& v, PHLANIMVAR& pav, SP pConfig, eAVarDamagePolicy policy) { constexpr const eAnimatedVarType EAVTYPE = typeToeAnimatedVarType; - const auto PAV = makeShared>(); + pav = makeUnique>(); - PAV->create(EAVTYPE, sc(this), PAV, v); - PAV->setConfig(pConfig); - PAV->m_Context.eDamagePolicy = policy; - - pav = std::move(PAV); + pav->create2(EAVTYPE, sc(this), pav, v); + pav->setConfig(pConfig); + pav->m_Context.eDamagePolicy = policy; } template diff --git a/src/managers/input/IdleInhibitor.cpp b/src/managers/input/IdleInhibitor.cpp index 0034a10fa..5750080c2 100644 --- a/src/managers/input/IdleInhibitor.cpp +++ b/src/managers/input/IdleInhibitor.cpp @@ -40,10 +40,10 @@ void CInputManager::recheckIdleInhibitorStatus() { auto WLSurface = Desktop::View::CWLSurface::fromResource(ii->inhibitor->m_surface.lock()); - if (!WLSurface) + if (!WLSurface || !WLSurface->view()) continue; - if (WLSurface->visible()) { + if (WLSurface->view()->aliveAndVisible()) { PROTO::idle->setInhibit(true); return; } @@ -85,10 +85,10 @@ bool CInputManager::isWindowInhibiting(const PHLWINDOW& w, bool onlyHl) { auto WLSurface = Desktop::View::CWLSurface::fromResource(surf); - if (!WLSurface) + if (!WLSurface || !WLSurface->view()) return; - if (WLSurface->visible()) + if (WLSurface->view()->aliveAndVisible()) *sc(data) = true; }, &isInhibiting); diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp index a42933ed8..4a24e861c 100644 --- a/src/protocols/core/DataDevice.cpp +++ b/src/protocols/core/DataDevice.cpp @@ -298,10 +298,17 @@ void CWLDataDeviceResource::sendDataOffer(SP offer) { void CWLDataDeviceResource::sendEnter(uint32_t serial, SP surf, const Vector2D& local, SP offer) { if (const auto WL = offer->getWayland(); WL) m_resource->sendEnterRaw(serial, surf->getResource()->resource(), wl_fixed_from_double(local.x), wl_fixed_from_double(local.y), WL->m_resource->resource()); + + m_entered = surf; + // FIXME: X11 } void CWLDataDeviceResource::sendLeave() { + if (!m_entered) + return; + + m_entered.reset(); m_resource->sendLeave(); } diff --git a/src/protocols/core/DataDevice.hpp b/src/protocols/core/DataDevice.hpp index 8b52933e3..b4ad378fa 100644 --- a/src/protocols/core/DataDevice.hpp +++ b/src/protocols/core/DataDevice.hpp @@ -111,8 +111,10 @@ class CWLDataDeviceResource : public IDataDevice { WP m_self; private: - SP m_resource; - wl_client* m_client = nullptr; + SP m_resource; + wl_client* m_client = nullptr; + + WP m_entered; friend class CWLDataDeviceProtocol; }; diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index ae01d2a11..198ba0e4a 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -849,7 +849,6 @@ void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, CFrameb } void CHyprOpenGLImpl::end() { - static auto PZOOMRIGID = CConfigValue("cursor:zoom_rigid"); static auto PZOOMDISABLEAA = CConfigValue("cursor:zoom_disable_aa"); TRACY_GPU_ZONE("RenderEnd"); @@ -861,20 +860,9 @@ void CHyprOpenGLImpl::end() { CBox monbox = {0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; - if (m_renderData.mouseZoomFactor != 1.f) { - const auto ZOOMCENTER = m_renderData.mouseZoomUseMouse ? - (g_pInputManager->getMouseCoordsInternal() - m_renderData.pMonitor->m_position) * m_renderData.pMonitor->m_scale : - m_renderData.pMonitor->m_transformedSize / 2.f; - - monbox.translate(-ZOOMCENTER).scale(m_renderData.mouseZoomFactor).translate(*PZOOMRIGID ? m_renderData.pMonitor->m_transformedSize / 2.0 : ZOOMCENTER); - - monbox.x = std::min(monbox.x, 0.0); - monbox.y = std::min(monbox.y, 0.0); - if (monbox.x + monbox.width < m_renderData.pMonitor->m_transformedSize.x) - monbox.x = m_renderData.pMonitor->m_transformedSize.x - monbox.width; - if (monbox.y + monbox.height < m_renderData.pMonitor->m_transformedSize.y) - monbox.y = m_renderData.pMonitor->m_transformedSize.y - monbox.height; - } + if (g_pHyprRenderer->m_renderMode == RENDER_MODE_NORMAL && m_renderData.mouseZoomFactor == 1.0f) + m_renderData.pMonitor->m_zoomController.m_resetCameraState = true; + m_renderData.pMonitor->m_zoomController.applyZoomTransform(monbox, m_renderData); m_applyFinalShader = !m_renderData.blockScreenShader; if (m_renderData.mouseZoomUseMouse && *PZOOMDISABLEAA) @@ -1715,7 +1703,9 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c const bool skipCM = !*PENABLECM || !m_cmSupported /* CM unsupported or disabled */ || m_renderData.pMonitor->doesNoShaderCM() /* no shader needed */ || (imageDescription == m_renderData.pMonitor->m_imageDescription && !data.cmBackToSRGB) /* Source and target have the same image description */ - || (((*PPASS && canPassHDRSurface) || (*PPASS == 1 && !isHDRSurface)) && m_renderData.pMonitor->inFullscreenMode()) /* Fullscreen window with pass cm enabled */; + || (((*PPASS && canPassHDRSurface) || + (*PPASS == 1 && !isHDRSurface && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR_EDID)) && + m_renderData.pMonitor->inFullscreenMode()) /* Fullscreen window with pass cm enabled */; if (!skipCM && !usingFinalShader && (texType == TEXTURE_RGBA || texType == TEXTURE_RGBX)) shader = &m_shaders->m_shCM; diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 8d0c48aa1..855a94398 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -355,7 +355,7 @@ class CHyprOpenGLImpl { GLsizei height = 0; } m_lastViewport; - std::array m_capStatus; + std::array m_capStatus = {}; std::vector m_drmFormats; bool m_hasModifiers = false; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 38cd951d9..b75dd1e0f 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -684,8 +684,9 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T return; } - if (!popup->visible()) + if (!popup->aliveAndVisible()) return; + const auto pos = popup->coordsRelativeToParent(); const Vector2D oldPos = renderdata.pos; renderdata.pos += pos; @@ -802,7 +803,7 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::s if (popups) { pLayer->m_popupHead->breadthfirst( [this, &renderdata](WP popup, void* data) { - if (!popup->visible()) + if (!popup->aliveAndVisible()) return; const auto SURF = popup->wlSurface()->resource(); @@ -1708,7 +1709,7 @@ void CHyprRenderer::renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace void CHyprRenderer::sendFrameEventsToWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now) { for (const auto& view : Desktop::View::getViewsForWorkspace(pWorkspace)) { - if (!view->visible()) + if (!view->aliveAndVisible()) continue; view->wlSurface()->resource()->frame(now); @@ -2505,7 +2506,7 @@ void CHyprRenderer::makeSnapshot(WP popup) { if (!PMONITOR || !PMONITOR->m_output || PMONITOR->m_pixelSize.x <= 0 || PMONITOR->m_pixelSize.y <= 0) return; - if (!popup->visible()) + if (!popup->aliveAndVisible()) return; Debug::log(LOG, "renderer: making a snapshot of {:x}", rc(popup.get()));