diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 46f2ca494..f56594c34 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -1030,10 +1030,9 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusTrue) { TEST_CASE(testScrollingViewBehaviourScheduledPropRefresh) { /* - Scheduled prop refresh must not move scrolling viewport. - The reason a prop refresh was queued is not saved, therefore it is not possible to clearly tell when and when not to move scrolling viewport - In this test, we test this by setting a workspace rule, which schedules a prop refresh - -------------------------------------------------------------------------------------------------------------------------------------- + Test that hl.exec_scheduled_prop_refresh_immediately() should immediately execute prop refresh. This is tested via inhibiting scrollin during helper functs dispatch; if it works, the viewport + should not move when a new workspace rule is created. If it doesn't, dispatch will miss because the refresh will be executed as another event + -------------------------------------------------------------------------------------------------------------------------------------- */ OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); @@ -1058,7 +1057,8 @@ TEST_CASE(testScrollingViewBehaviourScheduledPropRefresh) { OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:a' })")); // setting a workspace rule queues a doLater() call in the Event Loop Manager - OK(getFromSocket("/eval hl.workspace_rule({workspace = hl.get_active_workspace().id,gaps_in = 0})")); + OK(getFromSocket("/eval hl.dispatch(hl.dsp.layout('inhibit_scroll true')); hl.workspace_rule({workspace = hl.get_active_workspace().id,gaps_in = 0}); " + "hl.exec_scheduled_prop_refresh_immediately(); hl.dispatch(hl.dsp.layout('inhibit_scroll false'));")); // Check that the workspace rule is set ASSERT_CONTAINS(getFromSocket("/workspacerules"), "gapsIn: 0 0 0 0"); diff --git a/src/config/lua/LuaEventHandler.cpp b/src/config/lua/LuaEventHandler.cpp index 090f8b7d2..800fe2d5b 100644 --- a/src/config/lua/LuaEventHandler.cpp +++ b/src/config/lua/LuaEventHandler.cpp @@ -148,6 +148,9 @@ CLuaEventHandler::CLuaEventHandler(lua_State* L) : m_lua(L) { })); m_listeners.push_back(bus()->m_events.config.reloaded.listen([this] { dispatch("config.reloaded", 0, [](lua_State* L) {}); })); + m_listeners.push_back(bus()->m_events.config.props_refreshed.listen( + [this](const bool execdAsScheduled) { dispatch("config.props_refreshed", 1, [&](lua_State* L) { lua_pushboolean(L, sc(execdAsScheduled)); }); })); + m_listeners.push_back( bus()->m_events.keybinds.submap.listen([this](const std::string& submap) { dispatch("keybinds.submap", 1, [&](lua_State* L) { lua_pushstring(L, submap.c_str()); }); })); m_listeners.push_back(bus()->m_events.screenshare.state.listen([this](bool state, uint8_t type, const std::string& name) { @@ -277,6 +280,7 @@ std::unordered_set CLuaEventHandler::knownEvents() { "workspace.removed", "workspace.move_to_monitor", "config.reloaded", + "config.props_refreshed", "keybinds.submap", "screenshare.state", "hyprland.start", diff --git a/src/config/lua/bindings/LuaBindingsToplevel.cpp b/src/config/lua/bindings/LuaBindingsToplevel.cpp index 6e12cd786..63a815472 100644 --- a/src/config/lua/bindings/LuaBindingsToplevel.cpp +++ b/src/config/lua/bindings/LuaBindingsToplevel.cpp @@ -462,6 +462,11 @@ static int hlTimer(lua_State* L) { return 1; } +static int hlExecuteScheduledRefreshImmediately(lua_State* L) { + + return Supplementary::refresher()->executeScheduledRefreshImmediately(); +} + void Internal::registerToplevelBindings(lua_State* L, CConfigManager* mgr) { Internal::setMgrFn(L, mgr, "on", hlOn); Internal::setMgrFn(L, mgr, "bind", hlBind); @@ -473,5 +478,7 @@ void Internal::registerToplevelBindings(lua_State* L, CConfigManager* mgr) { Internal::setFn(L, "get_loaded_plugins", hlGetPlugins); Internal::setFn(L, "exec_cmd", hlExecCmd); + Internal::setFn(L, "exec_scheduled_prop_refresh_immediately", hlExecuteScheduledRefreshImmediately); + Internal::setFn(L, "unbind", hlUnbind); } diff --git a/src/config/supplementary/propRefresher/PropRefresher.cpp b/src/config/supplementary/propRefresher/PropRefresher.cpp index 73f51c2be..924066456 100644 --- a/src/config/supplementary/propRefresher/PropRefresher.cpp +++ b/src/config/supplementary/propRefresher/PropRefresher.cpp @@ -15,6 +15,7 @@ #include "../../shared/monitor/MonitorRuleManager.hpp" #include "../../shared/inotify/ConfigWatcher.hpp" +#include "../../../event/EventBus.hpp" using namespace Config; using namespace Config::Supplementary; @@ -25,111 +26,130 @@ UP& Supplementary::refresher() { } void CPropRefresher::scheduleRefresh(PropRefreshBits prop) { - static auto PZOOMFACTOR = CConfigValue("cursor:zoom_factor"); m_propsTripped |= prop; if (!m_scheduled && g_pEventLoopManager) { - g_pEventLoopManager->doLater([this, weak = WP{refresher()}] { + m_scheduledRefreshSeq = g_pEventLoopManager->doLater([this, weak = WP{refresher()}] { if (!weak) return; - - if (m_propsTripped & REFRESH_INPUT_DEVICES) { - g_pInputManager->setKeyboardLayout(); // update kb layout - g_pInputManager->setPointerConfigs(); // update mouse cfgs - g_pInputManager->setTouchDeviceConfigs(); // update touch device cfgs - g_pInputManager->setTabletConfigs(); // update tablets - g_pInputManager->setTabletToolConfigs(); // update tablettools - } - - if (m_propsTripped & REFRESH_SCREEN_SHADER) { - g_pHyprRenderer->m_reloadScreenShader = true; - for (auto const& m : State::monitorState()->monitors()) { - if (!m) - continue; - - m->m_forceFullFrames = 2; - m->scheduleFrame(); - } - } - - if (m_propsTripped & REFRESH_BLUR_FB) { - for (auto const& m : State::monitorState()->monitors()) { - if (!m) - continue; - - m->m_blurFBDirty = true; - m->m_forceFullFrames = 2; - m->scheduleFrame(); - } - } - - if (m_propsTripped & REFRESH_WINDOW_STATES) { - Desktop::Rule::ruleEngine()->updateAllRules(); - - for (const auto& ws : State::workspaceState()->workspaces()) { - if (!ws) - continue; - - ws->updateWindows(); - ws->updateWindowData(); - ws->updateWindowDecos(); - } - - g_pCompositor->updateAllWindowsAnimatedDecorationValues(); - - for (auto const& m : State::monitorState()->monitors()) { - if (!m) - continue; - - m->m_forceFullFrames = 2; - g_pHyprRenderer->damageMonitor(m); - m->scheduleFrame(); - } - } - - if (m_propsTripped & REFRESH_MONITOR_STATES) { - Config::monitorRuleMgr()->scheduleReload(); - Config::monitorRuleMgr()->ensureVRR(); - - for (const auto& m : State::monitorState()->monitors()) { - if (!m) - continue; - - g_layoutManager->recalculateMonitor(m, Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_PROP_REFRESH); - } - - State::workspacePlacementController()->ensurePersistentWorkspacesPresent( - nullptr, [](PHLWORKSPACE ws, PHLMONITOR mon, bool noWarp) { g_pCompositor->moveWorkspaceToMonitor(ws, mon, noWarp); }); - } - - if (m_propsTripped & REFRESH_LAYOUTS) { - Layout::Supplementary::algoMatcher()->updateWorkspaceLayouts(); - - for (auto const& m : State::monitorState()->monitors()) { - g_layoutManager->recalculateMonitor(m, Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_PROP_REFRESH); - g_pHyprRenderer->damageMonitor(m); - } - } - - if (m_propsTripped & REFRESH_CURSOR_ZOOMS) { - for (auto const& m : State::monitorState()->monitors()) { - *(m->m_cursorZoom) = *PZOOMFACTOR; - if (m->m_activeWorkspace) - m->m_activeWorkspace->m_space->recalculate(); - } - } - - if (m_propsTripped & REFRESH_CONFIG_WATCHER) - Config::watcher()->update(); - - if (m_propsTripped & REFRESH_GRADIENTS_GROUPBAR) - refreshGroupBarGradients(); - - m_scheduled = false; - m_propsTripped = 0; + refreshProp(true); }); m_scheduled = true; } } + +int CPropRefresher::executeScheduledRefreshImmediately() { + + if (!m_scheduled || m_scheduledRefreshSeq == 0) + return 1; + + g_pEventLoopManager->removeDoLater(m_scheduledRefreshSeq); + // m_scheduledRefreshSeq must be reset back to 0 during refreshProp() call + refreshProp(false); + return 0; +} + +void CPropRefresher::refreshProp(const bool execdAsScheduled) { + + static auto PZOOMFACTOR = CConfigValue("cursor:zoom_factor"); + + if (m_propsTripped & REFRESH_INPUT_DEVICES) { + g_pInputManager->setKeyboardLayout(); // update kb layout + g_pInputManager->setPointerConfigs(); // update mouse cfgs + g_pInputManager->setTouchDeviceConfigs(); // update touch device cfgs + g_pInputManager->setTabletConfigs(); // update tablets + g_pInputManager->setTabletToolConfigs(); // update tablettools + } + + if (m_propsTripped & REFRESH_SCREEN_SHADER) { + g_pHyprRenderer->m_reloadScreenShader = true; + for (auto const& m : State::monitorState()->monitors()) { + if (!m) + continue; + + m->m_forceFullFrames = 2; + m->scheduleFrame(); + } + } + + if (m_propsTripped & REFRESH_BLUR_FB) { + for (auto const& m : State::monitorState()->monitors()) { + if (!m) + continue; + + m->m_blurFBDirty = true; + m->m_forceFullFrames = 2; + m->scheduleFrame(); + } + } + + if (m_propsTripped & REFRESH_WINDOW_STATES) { + Desktop::Rule::ruleEngine()->updateAllRules(); + + for (const auto& ws : State::workspaceState()->workspaces()) { + if (!ws) + continue; + + ws->updateWindows(); + ws->updateWindowData(); + ws->updateWindowDecos(); + } + + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + + for (auto const& m : State::monitorState()->monitors()) { + if (!m) + continue; + + m->m_forceFullFrames = 2; + g_pHyprRenderer->damageMonitor(m); + m->scheduleFrame(); + } + } + + if (m_propsTripped & REFRESH_MONITOR_STATES) { + Config::monitorRuleMgr()->scheduleReload(); + Config::monitorRuleMgr()->ensureVRR(); + + for (const auto& m : State::monitorState()->monitors()) { + if (!m) + continue; + + g_layoutManager->recalculateMonitor(m); + } + + State::workspacePlacementController()->ensurePersistentWorkspacesPresent( + nullptr, [](PHLWORKSPACE ws, PHLMONITOR mon, bool noWarp) { g_pCompositor->moveWorkspaceToMonitor(ws, mon, noWarp); }); + } + + if (m_propsTripped & REFRESH_LAYOUTS) { + Layout::Supplementary::algoMatcher()->updateWorkspaceLayouts(); + + for (auto const& m : State::monitorState()->monitors()) { + g_layoutManager->recalculateMonitor(m); + g_pHyprRenderer->damageMonitor(m); + } + } + + if (m_propsTripped & REFRESH_CURSOR_ZOOMS) { + for (auto const& m : State::monitorState()->monitors()) { + *(m->m_cursorZoom) = *PZOOMFACTOR; + if (m->m_activeWorkspace) + m->m_activeWorkspace->m_space->recalculate(); + } + } + + if (m_propsTripped & REFRESH_CONFIG_WATCHER) + Config::watcher()->update(); + + if (m_propsTripped & REFRESH_GRADIENTS_GROUPBAR) + refreshGroupBarGradients(); + + m_scheduled = false; + m_scheduledRefreshSeq = 0; + m_propsTripped = 0; + + Event::bus()->m_events.config.props_refreshed.emit(execdAsScheduled); +} \ No newline at end of file diff --git a/src/config/supplementary/propRefresher/PropRefresher.hpp b/src/config/supplementary/propRefresher/PropRefresher.hpp index 695dd0c6b..382da6332 100644 --- a/src/config/supplementary/propRefresher/PropRefresher.hpp +++ b/src/config/supplementary/propRefresher/PropRefresher.hpp @@ -26,10 +26,14 @@ namespace Config::Supplementary { class CPropRefresher { public: void scheduleRefresh(PropRefreshBits reason); + int executeScheduledRefreshImmediately(); private: - bool m_scheduled = false; - PropRefreshBits m_propsTripped = 0; + void refreshProp(const bool execdAsScheduled); + + bool m_scheduled = false; + uint64_t m_scheduledRefreshSeq = 0; // 0 if no refresh event scheduled + PropRefreshBits m_propsTripped = 0; }; UP& refresher(); diff --git a/src/event/EventBus.hpp b/src/event/EventBus.hpp index d3c272737..c0a8af22d 100644 --- a/src/event/EventBus.hpp +++ b/src/event/EventBus.hpp @@ -165,8 +165,9 @@ namespace Event { } workspace; struct { - Event<> preReload; - Event<> reloaded; + Event<> preReload; + Event<> reloaded; + Event props_refreshed; } config; struct { diff --git a/src/layout/LayoutManager.hpp b/src/layout/LayoutManager.hpp index bd8d31100..1e509586f 100644 --- a/src/layout/LayoutManager.hpp +++ b/src/layout/LayoutManager.hpp @@ -52,7 +52,6 @@ namespace Layout { enum eRecalculateMonitorReason : uint8_t { RECALCULATE_MONITOR_REASON_UNKNOWN, // when the recalculate monitor reason is unknown or not important to preserve - RECALCULATE_MONITOR_REASON_PROP_REFRESH, RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE, RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE, RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN, diff --git a/src/layout/space/Space.cpp b/src/layout/space/Space.cpp index 46730e1ad..b661d092b 100644 --- a/src/layout/space/Space.cpp +++ b/src/layout/space/Space.cpp @@ -32,7 +32,7 @@ CSpace::CSpace(PHLWORKSPACE parent) : m_parent(parent) { recheckWorkArea(); if (m_algorithm) - m_algorithm->recalculate(RECALCULATE_REASON_CREATE_SPACE); + m_algorithm->recalculate(); }); } @@ -219,9 +219,9 @@ SP CSpace::getNextCandidate(SP old) { } bool Layout::isHardRecalculateReason(eRecalculateReason reason) { - return reason != RECALCULATE_REASON_CREATE_SPACE && reason != RECALCULATE_REASON_PROP_REFRESH && reason != RECALCULATE_REASON_WORKSPACE_CHANGE && - reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && reason != RECALCULATE_REASON_TOGGLE_LAYOUT_HANDLED_FULLSCREEN && - reason != RECALCULATE_REASON_TOGGLE_DEFAULT_HANDLED_FULLSCREEN && reason != RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES && reason != RECALCULATE_REASON_RENDER_MOINTOR; + return reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && + reason != RECALCULATE_REASON_TOGGLE_LAYOUT_HANDLED_FULLSCREEN && reason != RECALCULATE_REASON_TOGGLE_DEFAULT_HANDLED_FULLSCREEN && + reason != RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES && reason != RECALCULATE_REASON_RENDER_MOINTOR; } const std::vector>& CSpace::targets() const { @@ -234,7 +234,6 @@ eRecalculateReason Layout::recalcMonitorReasonToRecalcReason(CLayoutManager::eRe case CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE: return RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE; case CLayoutManager::RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE: return RECALCULATE_REASON_WORKSPACE_CHANGE; case CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN: return RECALCULATE_REASON_TOGGLE_DEFAULT_HANDLED_FULLSCREEN; - case CLayoutManager::RECALCULATE_MONITOR_REASON_PROP_REFRESH: return RECALCULATE_REASON_PROP_REFRESH; default: return RECALCULATE_REASON_UNKNOWN; } } \ No newline at end of file diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp index ce0d1af76..1d0ea575f 100644 --- a/src/layout/space/Space.hpp +++ b/src/layout/space/Space.hpp @@ -15,8 +15,6 @@ namespace Layout { enum eRecalculateReason : uint8_t { RECALCULATE_REASON_UNKNOWN, // when the recalculate reason is unknown or not important to preserve - RECALCULATE_REASON_PROP_REFRESH, - RECALCULATE_REASON_CREATE_SPACE, RECALCULATE_REASON_WORKSPACE_CHANGE, RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE, RECALCULATE_REASON_TOGGLE_DEFAULT_HANDLED_FULLSCREEN,