From face6d144be7e33c7f49798d8cc5e8fe416de43c Mon Sep 17 00:00:00 2001 From: erStarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 7 Aug 2026 16:53:28 +0200 Subject: [PATCH] core/fullscreen: set pos/size of all group members to the same value when FSing groups (#15577) * Fix group members not having uniform size when FSed * fix the same bug - but for scrolling * format * fix include * ditto * groups are evil * format * TEST - Default handled FS -- tested in dwindle Co-authored-by: Aqa-Ib <16420574+Aqa-Ib@users.noreply.github.com> * TEST - Default handled FS -- tested in scrolling Co-authored-by: Aqa-Ib <16420574+Aqa-Ib@users.noreply.github.com> * TEST - layout handled FS -- Scrolling Co-authored-by: Aqa-Ib <16420574+Aqa-Ib@users.noreply.github.com> * format and comment fix * spelling is hard * slight fix for posterity * spelling is very hard * thankyouclangdverycool * revert change that made all windows in a group show up as FS in hyprctl clients * change test to reflect the reversion * mirror mirror can you tell me who forgot to revert that change after it was redundant? * tribute to the magic mirror * clean up includes * format * Hope is the first step on the road to disappointment Fix tests failing --------- Co-authored-by: Aqa-Ib <16420574+Aqa-Ib@users.noreply.github.com> --- hyprtester/src/tests/main/dwindle.cpp | 178 +++++++ hyprtester/src/tests/main/scroll.cpp | 497 +++++++++++++++++- src/desktop/rule/windowRule/WindowRule.cpp | 11 +- src/desktop/view/Group.cpp | 1 - .../tiled/scrolling/ScrollingAlgorithm.cpp | 1 + .../scrolling/ScrollingFullscreenHandler.cpp | 19 +- src/layout/target/WindowTarget.cpp | 41 +- .../fullscreen/handler/FullscreenHandler.cpp | 21 +- 8 files changed, 736 insertions(+), 33 deletions(-) diff --git a/hyprtester/src/tests/main/dwindle.cpp b/hyprtester/src/tests/main/dwindle.cpp index 683060530..9a5896bb1 100644 --- a/hyprtester/src/tests/main/dwindle.cpp +++ b/hyprtester/src/tests/main/dwindle.cpp @@ -344,6 +344,184 @@ TEST_CASE(dwindleFullscreenMaximiseDispatchers) { } } +TEST_CASE(dwindleTestFsingGroupedWindows) { + + // Shared test among all default handled FS + + /* + When a group of windows are FSed, all the windows in the group are expected to have the same size + */ + + OK(getFromSocket("/eval hl.config({ general = { layout = 'dwindle' } })")); + + OK(getFromSocket("/eval hl.config({ group = { auto_group = true } })")); + + // Config opt for adding gaps_out and border_size and a workspace rule that removes them from maximised windows to make sure maximise works properly + + OK(getFromSocket("r/eval hl.config({ general = { gaps_out = 10, border_size = 10 } })")); + OK(getFromSocket("/eval hl.workspace_rule({ workspace = 'f[1]', gaps_out = 0, border_size = 0 })")); + + ASSERT(Tests::windowCount(), 0); + auto kitten1 = Tests::spawnKitty("kitten1"); + if (!kitten1) { + FAIL_TEST("Could not spawn kitty"); + } + OK(getFromSocket("/dispatch hl.dsp.group.toggle()")); + + auto kitten2 = Tests::spawnKitty("kitten2"); + if (!kitten2) { + FAIL_TEST("Could not spawn kitty"); + } + ASSERT(Tests::windowCount(), 2); + + // Fullscreen + { + auto checkHiddenGroupMember = [&](const std::string& targetKitten) { + auto clients = getFromSocket("/clients"); + auto classPos = clients.find("class: " + targetKitten); + if (classPos == std::string::npos) { + FAIL_TEST("Could not find specific kitten in clients output"); + } else { + auto entryStart = clients.rfind("Window ", classPos); + auto entryEnd = clients.find("\n\n", classPos); + auto windowEntry = clients.substr(entryStart, entryEnd - entryStart); + EXPECT_CONTAINS(windowEntry, "size: 1920,1080"); + EXPECT_CONTAINS(windowEntry, "at: 0,0"); + } + }; + + // Tiled + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'set', layout_aware = false })")); + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember("kitten1"); + + // try switching to next window in group + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember("kitten2"); + + // go back for next test + OK(getFromSocket("/dispatch hl.dsp.group.prev()")); + // unset FS for next test + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'unset', layout_aware = false })")); + + // floating + OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set' })")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'set', layout_aware = false })")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember("kitten1"); + + // try switching to next window in group + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember("kitten2"); + + // go back for next test + OK(getFromSocket("/dispatch hl.dsp.group.prev()")); + + // prep for next test + OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'unset' })")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'unset', layout_aware = false })")); + } + + // Maximized + { + auto checkHiddenGroupMember = [&](const std::string& targetKitten) { + auto clients = getFromSocket("/clients"); + auto classPos = clients.find("class: " + targetKitten); + if (classPos == std::string::npos) { + FAIL_TEST("Could not find specific kitten in clients output"); + } else { + auto entryStart = clients.rfind("Window ", classPos); + auto entryEnd = clients.find("\n\n", classPos); + auto windowEntry = clients.substr(entryStart, entryEnd - entryStart); + EXPECT_CONTAINS(windowEntry, "size: 1920,1059"); + EXPECT_CONTAINS(windowEntry, "at: 0,21"); + } + }; + + // Tiled + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'maximized', action = 'set', layout_aware = false })")); + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: 0,21"); + EXPECT_CONTAINS(str, "size: 1920,1059"); + } + checkHiddenGroupMember("kitten1"); + + // try switching to next window in group + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: 0,21"); + EXPECT_CONTAINS(str, "size: 1920,1059"); + } + checkHiddenGroupMember("kitten2"); + + // go back for next test + OK(getFromSocket("/dispatch hl.dsp.group.prev()")); + // unset FS for next test + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'maximized', action = 'unset', layout_aware = false })")); + + // floating + OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set' })")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'maximized', action = 'set', layout_aware = false })")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: 0,21"); + EXPECT_CONTAINS(str, "size: 1920,1059"); + } + checkHiddenGroupMember("kitten1"); + + // try switching to next window in group + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: 0,21"); + EXPECT_CONTAINS(str, "size: 1920,1059"); + } + checkHiddenGroupMember("kitten2"); + } +} + TEST_CASE(dwindleTestFsFocusUnderFSWindow) { // Shared test among all default handled FS diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 9244bccbe..fc142d0f2 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -346,6 +346,323 @@ TEST_CASE(scroll_LAYOUT_HANDLED_maximized) { } } +TEST_CASE(scroll_LAYOUT_HANDLED_TestFsingGroupedWindows) { + + /* + When a group of windows are FSed, all the windows in the group are expected to have the same size + + In scrolling's layout FS handling, this is expected to hold true even when we scroll away from the window + + */ + + OK(getFromSocket("/eval hl.config({ general = { layout = 'scrolling' } })")); + + OK(getFromSocket("/eval hl.config({ group = { auto_group = true } })")); + + OK(getFromSocket("/eval hl.config({ scrolling = { follow_focus = false } })")); + OK(getFromSocket("/eval hl.config({ scrolling = { wrap_swapcol = false } })")); + + // Config opt for adding gaps_out and border_size and a workspace rule that removes them from maximised windows to make sure maximise works properly + + OK(getFromSocket("r/eval hl.config({ general = { gaps_out = 10, border_size = 10 } })")); + OK(getFromSocket("/eval hl.workspace_rule({ workspace = 'f[1]', gaps_out = 0, border_size = 0 })")); + + // 2 grouped windows, 1 ungrouped window + ASSERT(Tests::windowCount(), 0); + auto kitten1 = Tests::spawnKitty("kitten1"); + if (!kitten1) { + FAIL_TEST("Could not spawn kitty"); + } + OK(getFromSocket("/dispatch hl.dsp.group.toggle()")); + + auto kitten2 = Tests::spawnKitty("kitten2"); + if (!kitten2) { + FAIL_TEST("Could not spawn kitty"); + } + + OK(getFromSocket("/eval hl.config({ group = { auto_group = false } })")); + + auto kitten3 = Tests::spawnKitty("kitten3"); + if (!kitten3) { + FAIL_TEST("Could not spawn kitty"); + } + + ASSERT(Tests::windowCount(), 3); + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:kitten2'})")); + + // Fullscreen + { + auto checkHiddenGroupMember = [&](const std::string& targetKitten) { + auto clients = getFromSocket("/clients"); + auto classPos = clients.find("class: " + targetKitten); + if (classPos == std::string::npos) { + FAIL_TEST("Could not find specific kitten in clients output"); + } else { + auto entryStart = clients.rfind("Window ", classPos); + auto entryEnd = clients.find("\n\n", classPos); + auto windowEntry = clients.substr(entryStart, entryEnd - entryStart); + EXPECT_CONTAINS(windowEntry, "size: 1920,1080"); + EXPECT_CONTAINS(windowEntry, "at: 0,0"); + } + }; + + auto checkHiddenGroupMember_notCovering = [&](const std::string& targetKitten) { + auto clients = getFromSocket("/clients"); + auto classPos = clients.find("class: " + targetKitten); + if (classPos == std::string::npos) { + FAIL_TEST("Could not find specific kitten in clients output"); + } else { + auto entryStart = clients.rfind("Window ", classPos); + auto entryEnd = clients.find("\n\n", classPos); + auto windowEntry = clients.substr(entryStart, entryEnd - entryStart); + EXPECT_CONTAINS(windowEntry, "at: -960,0"); + // fullscreen windows are MONBOX sized so they disregard borders and gaps_out even when not covering + EXPECT_CONTAINS(windowEntry, "size: 1920,1080"); + EXPECT_CONTAINS(windowEntry, "fullscreen: 0"); + } + }; + + // Tiled + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'set', layout_aware = true })")); + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember("kitten1"); + + // try switching to next window in group + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember("kitten2"); + + // go back for next test + OK(getFromSocket("/dispatch hl.dsp.group.prev()")); + + // Try scrolling away + OK(getFromSocket("/dispatch hl.dsp.layout('focus r')")); + + // not scrolling onto it because follow_focus = false + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:kitten2'})")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: -960,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember_notCovering("kitten1"); + + // atm the viewport moves if you switch to next window in group, this it to compensate + OK(getFromSocket("/dispatch hl.dsp.layout('inhibit_scroll 1')")); + // try switching to next window in group when you have scrolled away + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + OK(getFromSocket("/dispatch hl.dsp.layout('inhibit_scroll 0')")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: -960,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember_notCovering("kitten2"); + + // go back for next test + OK(getFromSocket("/dispatch hl.dsp.group.prev()")); + // re-establish covering status of FS group for the next test + OK(getFromSocket("/eval hl.config({ scrolling = { follow_focus = true } })")); + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:kitten2'})")); + OK(getFromSocket("/eval hl.config({ scrolling = { follow_focus = false } })")); + // unset FS for next test + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'unset', layout_aware = true })")); + + // floating + OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set' })")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'set', layout_aware = true })")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember("kitten1"); + + // try switching to next window in group + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember("kitten2"); + + // go back for next test + OK(getFromSocket("/dispatch hl.dsp.group.prev()")); + + // prep for next test + OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'unset' })")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'unset', layout_aware = true })")); + // The window may have been dropped from floating to tiling in the wrong place. make sure it is the leftmost + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:kitten2'})")); + OK(getFromSocket("/dispatch hl.dsp.layout('swapcol l')")); + } + + // Maximized + { + auto checkHiddenGroupMember_tiled = [&](const std::string& targetKitten) { + auto clients = getFromSocket("/clients"); + auto classPos = clients.find("class: " + targetKitten); + if (classPos == std::string::npos) { + FAIL_TEST("Could not find specific kitten in clients output"); + } else { + auto entryStart = clients.rfind("Window ", classPos); + auto entryEnd = clients.find("\n\n", classPos); + auto windowEntry = clients.substr(entryStart, entryEnd - entryStart); + EXPECT_CONTAINS(windowEntry, "size: 1915,1059"); + EXPECT_CONTAINS(windowEntry, "at: 0,21"); + } + }; + + auto checkHiddenGroupMember_floating = [&](const std::string& targetKitten) { + auto clients = getFromSocket("/clients"); + auto classPos = clients.find("class: " + targetKitten); + if (classPos == std::string::npos) { + FAIL_TEST("Could not find specific kitten in clients output"); + } else { + auto entryStart = clients.rfind("Window ", classPos); + auto entryEnd = clients.find("\n\n", classPos); + auto windowEntry = clients.substr(entryStart, entryEnd - entryStart); + EXPECT_CONTAINS(windowEntry, "size: 1920,1059"); + EXPECT_CONTAINS(windowEntry, "at: 0,21"); + } + }; + + auto checkHiddenGroupMember_notCovering = [&](const std::string& targetKitten) { + auto clients = getFromSocket("/clients"); + auto classPos = clients.find("class: " + targetKitten); + if (classPos == std::string::npos) { + FAIL_TEST("Could not find specific kitten in clients output"); + } else { + auto entryStart = clients.rfind("Window ", classPos); + auto entryEnd = clients.find("\n\n", classPos); + auto windowEntry = clients.substr(entryStart, entryEnd - entryStart); + EXPECT_CONTAINS(windowEntry, "at: -940,41"); + // cuz the gaps and borders workspace rule now applies as it is no longer covering + EXPECT_CONTAINS(windowEntry, "size: 1875,1019"); + EXPECT_CONTAINS(windowEntry, "fullscreen: 0"); + } + }; + + // Tiled + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'maximized', action = 'set', layout_aware = true })")); + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: 0,21"); + EXPECT_CONTAINS(str, "size: 1915,1059"); + } + checkHiddenGroupMember_tiled("kitten1"); + + // try switching to next window in group + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: 0,21"); + EXPECT_CONTAINS(str, "size: 1915,1059"); + } + checkHiddenGroupMember_tiled("kitten2"); + + // go back for next test + OK(getFromSocket("/dispatch hl.dsp.group.prev()")); + + // Try scrolling away + OK(getFromSocket("/dispatch hl.dsp.layout('focus r')")); + + // not scrolling onto it because follow_focus = false + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:kitten2'})")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: -940,41"); + EXPECT_CONTAINS(str, "size: 1875,1019"); + EXPECT_CONTAINS(str, "fullscreen: 1"); + } + checkHiddenGroupMember_notCovering("kitten1"); + + // atm the viewport moves if you switch to next window in group, this it to compensate + OK(getFromSocket("/dispatch hl.dsp.layout('inhibit_scroll 1')")); + // try switching to next window in group when you have scrolled away + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + OK(getFromSocket("/dispatch hl.dsp.layout('inhibit_scroll 0')")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: -940,41"); + EXPECT_CONTAINS(str, "size: 1875,1019"); + EXPECT_CONTAINS(str, "fullscreen: 1"); + } + checkHiddenGroupMember_notCovering("kitten2"); + + // go back for next test + OK(getFromSocket("/dispatch hl.dsp.group.prev()")); + // re-establish covering status of FS group for the next test + OK(getFromSocket("/eval hl.config({ scrolling = { follow_focus = true } })")); + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:kitten2'})")); + OK(getFromSocket("/eval hl.config({ scrolling = { follow_focus = false } })")); + // unset FS for next test + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'maximized', action = 'unset', layout_aware = true })")); + + // floating + OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set' })")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'maximized', action = 'set', layout_aware = true })")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: 0,21"); + EXPECT_CONTAINS(str, "size: 1920,1059"); + } + checkHiddenGroupMember_floating("kitten1"); + + // try switching to next window in group + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: 0,21"); + EXPECT_CONTAINS(str, "size: 1920,1059"); + } + checkHiddenGroupMember_floating("kitten2"); + } +} + TEST_CASE(scroll_LAYOUT_HANDLED_fullscreenRetainsGeometryWhileScrolling) { OK(getFromSocket("/eval hl.config({ general = { layout = 'scrolling' } })")); @@ -1075,7 +1392,7 @@ TEST_CASE(scroll_LAYOUT_HANDLED_focusInDirectionFocusFollowFocusTrue) { // if on_focus_under_fullscreen = 0 focus({direction}) is disallowed from moving focus from FS window // if on_focus_under_fullscreen = 1/2, standard behaviour of the config option won't be followed but focus will move in the firection specified as if window was not FS - OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + OK(getFromSocket("/eval hl.config({ general = { layout = 'scrolling' } })")); /* This test serves as a test for all layouts that use deafult FS behaviour @@ -1396,6 +1713,184 @@ TEST_CASE(scroll_DEFAULT_HANDLED_fullscreenMaximiseDispatchers) { } } +TEST_CASE(scroll_DEFAULT_HANDLED_TestFsingGroupedWindows) { + + // Shared test among all default handled FS + + /* + When a group of windows are FSed, all the windows in the group are expected to have the same size + */ + + OK(getFromSocket("/eval hl.config({ general = { layout = 'scrolling' } })")); + + OK(getFromSocket("/eval hl.config({ group = { auto_group = true } })")); + + // Config opt for adding gaps_out and border_size and a workspace rule that removes them from maximised windows to make sure maximise works properly + + OK(getFromSocket("r/eval hl.config({ general = { gaps_out = 10, border_size = 10 } })")); + OK(getFromSocket("/eval hl.workspace_rule({ workspace = 'f[1]', gaps_out = 0, border_size = 0 })")); + + ASSERT(Tests::windowCount(), 0); + auto kitten1 = Tests::spawnKitty("kitten1"); + if (!kitten1) { + FAIL_TEST("Could not spawn kitty"); + } + OK(getFromSocket("/dispatch hl.dsp.group.toggle()")); + + auto kitten2 = Tests::spawnKitty("kitten2"); + if (!kitten2) { + FAIL_TEST("Could not spawn kitty"); + } + ASSERT(Tests::windowCount(), 2); + + // Fullscreen + { + auto checkHiddenGroupMember = [&](const std::string& targetKitten) { + auto clients = getFromSocket("/clients"); + auto classPos = clients.find("class: " + targetKitten); + if (classPos == std::string::npos) { + FAIL_TEST("Could not find specific kitten in clients output"); + } else { + auto entryStart = clients.rfind("Window ", classPos); + auto entryEnd = clients.find("\n\n", classPos); + auto windowEntry = clients.substr(entryStart, entryEnd - entryStart); + EXPECT_CONTAINS(windowEntry, "size: 1920,1080"); + EXPECT_CONTAINS(windowEntry, "at: 0,0"); + } + }; + + // Tiled + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'set', layout_aware = false })")); + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember("kitten1"); + + // try switching to next window in group + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember("kitten2"); + + // go back for next test + OK(getFromSocket("/dispatch hl.dsp.group.prev()")); + // unset FS for next test + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'unset', layout_aware = false })")); + + // floating + OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set' })")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'set', layout_aware = false })")); + + { + // check position and size for the focused group member. + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember("kitten1"); + + // try switching to next window in group + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + + { + // check position and size for the focused group member. + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,1080"); + } + checkHiddenGroupMember("kitten2"); + + // go back for next test + OK(getFromSocket("/dispatch hl.dsp.group.prev()")); + + // prep for next test + OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'unset' })")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'unset', layout_aware = false })")); + } + + // Maximized + { + auto checkHiddenGroupMember = [&](const std::string& targetKitten) { + auto clients = getFromSocket("/clients"); + auto classPos = clients.find("class: " + targetKitten); + if (classPos == std::string::npos) { + FAIL_TEST("Could not find specific kitten in clients output"); + } else { + auto entryStart = clients.rfind("Window ", classPos); + auto entryEnd = clients.find("\n\n", classPos); + auto windowEntry = clients.substr(entryStart, entryEnd - entryStart); + EXPECT_CONTAINS(windowEntry, "size: 1920,1059"); + EXPECT_CONTAINS(windowEntry, "at: 0,21"); + } + }; + + // Tiled + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'maximized', action = 'set', layout_aware = false })")); + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: 0,21"); + EXPECT_CONTAINS(str, "size: 1920,1059"); + } + checkHiddenGroupMember("kitten1"); + + // try switching to next window in group + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + + { + // check position and size for the focused group member. + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: 0,21"); + EXPECT_CONTAINS(str, "size: 1920,1059"); + } + checkHiddenGroupMember("kitten2"); + + // go back for next test + OK(getFromSocket("/dispatch hl.dsp.group.prev()")); + // unset FS for next test + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'maximized', action = 'unset', layout_aware = false })")); + + // floating + OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set' })")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'maximized', action = 'set', layout_aware = false })")); + + { + // check position and size for the focused group member (kitten2). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten2"); + EXPECT_CONTAINS(str, "at: 0,21"); + EXPECT_CONTAINS(str, "size: 1920,1059"); + } + checkHiddenGroupMember("kitten1"); + + // try switching to next window in group + OK(getFromSocket("/dispatch hl.dsp.group.next()")); + + { + // check position and size for the focused group member (kitten1). + auto str = getFromSocket("/activewindow"); + EXPECT_CONTAINS(str, "class: kitten1"); + EXPECT_CONTAINS(str, "at: 0,21"); + EXPECT_CONTAINS(str, "size: 1920,1059"); + } + checkHiddenGroupMember("kitten2"); + } +} + TEST_CASE(scroll_DEFAULT_HANDLED_testFsFocusUnderFSWindow) { // Shared test among all default handled FS diff --git a/src/desktop/rule/windowRule/WindowRule.cpp b/src/desktop/rule/windowRule/WindowRule.cpp index 44fdf6930..981a326de 100644 --- a/src/desktop/rule/windowRule/WindowRule.cpp +++ b/src/desktop/rule/windowRule/WindowRule.cpp @@ -8,7 +8,7 @@ #include "../../../desktop/state/FocusState.hpp" #include "../../../protocols/types/ContentType.hpp" #include "../../../config/shared/parserUtils/ParserUtils.hpp" -#include "desktop/rule/windowRule/WindowRuleEffectContainer.hpp" +#include "../../../desktop/view/Group.hpp" #include #include @@ -415,7 +415,8 @@ bool CWindowRule::matches(PHLWINDOW w, bool allowEnvLookup) { return false; break; case RULE_PROP_FULLSCREEN: - if (!engine->match(Fullscreen::controller()->isFullscreen(w))) + // FS states of a group are owned by the current window of the group + if (!engine->match(Fullscreen::controller()->isFullscreen(w->m_group ? w->m_group->current() : w))) return false; break; case RULE_PROP_PINNED: @@ -435,11 +436,13 @@ bool CWindowRule::matches(PHLWINDOW w, bool allowEnvLookup) { return false; break; case RULE_PROP_FULLSCREENSTATE_INTERNAL: - if (!engine->match(Fullscreen::controller()->getFullscreenModes(w).internal)) + // FS states of a group are owned by the current window of the group + if (!engine->match(Fullscreen::controller()->getFullscreenModes(w->m_group ? w->m_group->current() : w).internal)) return false; break; case RULE_PROP_FULLSCREENSTATE_CLIENT: - if (!engine->match(Fullscreen::controller()->getFullscreenModes(w).client)) + // FS states of a group are owned by the current window of the group + if (!engine->match(Fullscreen::controller()->getFullscreenModes(w->m_group ? w->m_group->current() : w).client)) return false; break; case RULE_PROP_ON_WORKSPACE: diff --git a/src/desktop/view/Group.cpp b/src/desktop/view/Group.cpp index 4777a2ebf..1fb7e2cdb 100644 --- a/src/desktop/view/Group.cpp +++ b/src/desktop/view/Group.cpp @@ -258,7 +258,6 @@ void CGroup::setCurrent(size_t idx) { if (IS_FULLSCREEN) { Fullscreen::controller()->setFullscreenMode(newWindow, FS_MODE_INTERNAL, std::nullopt, IS_LAYOUT_HANDLED); newWindow->m_target->warpPositionSize(); - oldWindow->m_target->setPositionGlobal(newWindow->m_target->position()); // TODO: this is a hack and sucks } if (WASFOCUS) diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index 4aa118548..6ff3a8de4 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -552,6 +552,7 @@ void SScrollingData::recalculate(bool forceInstant) { Fullscreen::controller()->m_windowPosSettingQueued = true; // must set pos of the highest level target (i.e. if target a part of a group, must set that group's pos which will set the pos of all member targets) TDATA->target->setPositionGlobal(targetBoxWithGaps(TDATA->layoutBox, i, j, COL_HAS_FS_TARGET && TARGET_FS_MODE == Fullscreen::FSMODE_FULLSCREEN)); + Fullscreen::controller()->m_windowPosSettingQueued = false; } if (forceInstant && TDATA->target) diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingFullscreenHandler.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingFullscreenHandler.cpp index 17f6f5207..5c771d635 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingFullscreenHandler.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingFullscreenHandler.cpp @@ -45,7 +45,7 @@ bool CScrollingFullscreenHandler::isFullscreen(SP target, const if (!target) return false; - // A window group's FS modes are considered to be owned by its current window + // A window group's FS state is considered to be owned by its current window if (const auto WINDOW_GROUP_TARGET = dc(target.get()); WINDOW_GROUP_TARGET && target->type() == Layout::TARGET_TYPE_GROUP) { if (WINDOW_GROUP_TARGET->getGroup() && WINDOW_GROUP_TARGET->getGroup()->current() && WINDOW_GROUP_TARGET->getGroup()->current()->m_target) target = WINDOW_GROUP_TARGET->getGroup()->current()->m_target; @@ -96,6 +96,7 @@ SFullscreenMode CScrollingFullscreenHandler::getFullscreenModes(SP(target.get()); WINDOW_GROUP_TARGET && target->type() == Layout::TARGET_TYPE_GROUP) { if (WINDOW_GROUP_TARGET->getGroup() && WINDOW_GROUP_TARGET->getGroup()->current() && WINDOW_GROUP_TARGET->getGroup()->current()->m_target) target = WINDOW_GROUP_TARGET->getGroup()->current()->m_target; @@ -288,9 +289,18 @@ void CScrollingFullscreenHandler::updateTargetRulesAndDecos(const SPworkspace()->m_monitor.lock(); const auto WINDOW = target->window(); - WINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT | - Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE); - WINDOW->updateDecorationValues(); + // If window is in a group, we need to update these values for ALL members of the group. + if (WINDOW->m_group) { + for (const auto& gm : WINDOW->m_group->windows()) { + gm->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT | + Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE); + gm->updateDecorationValues(); + } + } else { + WINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT | + Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE); + WINDOW->updateDecorationValues(); + } // Normally, FS controller's FS state setter's method of handling window rules should be used; but calling g_layoutManager->recalculateMonitor(MONITOR) and getSpace()->recalculate() // here would lead to an inf recursion @@ -565,7 +575,6 @@ eFullscreenHandler CScrollingFullscreenHandler::getFullscreenHandlerName() const void CScrollingFullscreenHandler::sScrollingDataRecalculateHelper(const SP CURRENT_FS_TDATA, const PHLMONITOR MONITOR, const bool TARGET_WORKSPACE_HAS_FS) { - // TODO Decouple FS logic from SScrollingData::recalculate() to avoid having to schedule a prop refresh: it has to be here and it's a mess because recalculate() handled scrolling // onto/away from FS windows and this process doesn't call the controller's FS setters which are normally responsible for handling window rule checks. diff --git a/src/layout/target/WindowTarget.cpp b/src/layout/target/WindowTarget.cpp index 461ce098f..8ed9980a5 100644 --- a/src/layout/target/WindowTarget.cpp +++ b/src/layout/target/WindowTarget.cpp @@ -13,6 +13,7 @@ #include "../../state/MonitorState.hpp" #include "../../desktop/Workspace.hpp" #include "../../managers/fullscreen/FullscreenController.hpp" +#include "../../desktop/view/Group.hpp" #include @@ -44,6 +45,11 @@ void CWindowTarget::updatePos(uint8_t flags) { if (!m_window) return; + const auto effectiveWindow = [&]() { + // FS state of a window in a group is owned by the current window of that group. + return window()->m_group ? window()->m_group->current() : window(); + }; + g_pHyprRenderer->damageWindow(m_window.lock()); CScopeGuard x([this] { g_pHyprRenderer->damageWindow(m_window.lock()); }); @@ -67,7 +73,7 @@ void CWindowTarget::updatePos(uint8_t flags) { } // Non-FS Floating Windows - if (floating() && m_window && !Fullscreen::controller()->isFullscreen(m_window.lock())) { + if (floating() && m_window && !Fullscreen::controller()->isFullscreen(effectiveWindow())) { m_window->setBox(m_box.logicalBox); if (CONFIGURECLIENT) @@ -79,17 +85,15 @@ void CWindowTarget::updatePos(uint8_t flags) { /* FS Handling */ - // prevent re-setting an FS window's pos after it is set by the FS calls - if (m_window && Fullscreen::controller()->isFullscreen(m_window.lock(), std::nullopt, true)) { + // prevent re-setting a covering FS window's pos after it is set by the FS calls + if (m_window && Fullscreen::controller()->isFullscreen(effectiveWindow(), std::nullopt, true)) { if (!Fullscreen::controller()->m_windowPosSettingQueued) return; - Fullscreen::controller()->m_windowPosSettingQueued = false; } // Default Handled FS (floating or tiling) - if (const auto FSMODES = Fullscreen::controller()->getFullscreenModes(m_window.lock()); - FSMODES.internal != Fullscreen::FSMODE_NONE && !Fullscreen::controller()->layoutManagedFS(m_self->window())) { - + if (const auto FSMODES = Fullscreen::controller()->getFullscreenModes(effectiveWindow()); + FSMODES.internal != Fullscreen::FSMODE_NONE && !Fullscreen::controller()->layoutManagedFS(effectiveWindow())) { if (FSMODES.internal == Fullscreen::FSMODE_FULLSCREEN) { m_window->setBox(m_box.logicalBox); @@ -100,7 +104,8 @@ void CWindowTarget::updatePos(uint8_t flags) { visualBox.round(); // Reserved area must be updated before this is called - const auto RESERVED = m_window->getFullWindowReservedArea(); + // Reserved area for all windows in a group are owned by the leading window. Other windows are hidden anyway so this simply ensures their sizes are uniform when FSed + const auto RESERVED = effectiveWindow()->getFullWindowReservedArea(); m_window->setBox({visualBox.pos() + RESERVED.topLeft, visualBox.size() - (RESERVED.topLeft + RESERVED.bottomRight)}); } @@ -111,9 +116,9 @@ void CWindowTarget::updatePos(uint8_t flags) { return; } - // Layout handled FS - if (const auto FSMODES = Fullscreen::controller()->getFullscreenModes(m_window.lock()); - FSMODES.internal != Fullscreen::FSMODE_NONE && Fullscreen::controller()->layoutManagedFS(m_self->window())) { + // Layout handled FS (Tiled Only) + if (const auto FSMODES = Fullscreen::controller()->getFullscreenModes(effectiveWindow()); + FSMODES.internal != Fullscreen::FSMODE_NONE && Fullscreen::controller()->layoutManagedFS(effectiveWindow())) { CBox nodeBox = m_box.logicalBox; CBox visualBox = m_box.visualBox.empty() ? nodeBox : m_box.visualBox; @@ -124,7 +129,8 @@ void CWindowTarget::updatePos(uint8_t flags) { } else if (FSMODES.internal == Fullscreen::FSMODE_MAXIMIZED) { // Reserved area must be updated before this is called - const auto RESERVED = m_window->getFullWindowReservedArea(); + // Reserved area for all windows in a group are owned by the leading window. Other windows are hidden anyway so this simply ensures their sizes are uniform when FSed + const auto RESERVED = effectiveWindow()->getFullWindowReservedArea(); m_window->setBox({visualBox.pos() + RESERVED.topLeft, visualBox.size() - (RESERVED.topLeft + RESERVED.bottomRight)}); } @@ -166,7 +172,7 @@ void CWindowTarget::updatePos(uint8_t flags) { Vector2D ratioPadding; - if ((*REQUESTEDRATIO).y != 0 && m_space->algorithm()->tiledTargets() <= 1 && m_window && !Fullscreen::controller()->isFullscreen(m_window.lock())) { + if ((*REQUESTEDRATIO).y != 0 && m_space->algorithm()->tiledTargets() <= 1 && m_window) { const Vector2D originalSize = MONITOR_WORKAREA.size(); const double requestedRatio = (*REQUESTEDRATIO).x / (*REQUESTEDRATIO).y; @@ -194,7 +200,7 @@ void CWindowTarget::updatePos(uint8_t flags) { calcSize = calcSize - GAPOFFSETTOPLEFT - GAPOFFSETBOTTOMRIGHT - ratioPadding; } - if (isPseudo() && m_window && !Fullscreen::controller()->isFullscreen(m_window.lock())) { + if (isPseudo() && m_window) { // Calculate pseudo float scale = 1; @@ -226,9 +232,8 @@ void CWindowTarget::updatePos(uint8_t flags) { if (*PCLAMP_TILED) { Vector2D minSize = m_window->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}); - Vector2D maxSize = - Fullscreen::controller()->isFullscreen(m_window.lock()) ? Vector2D{INFINITY, INFINITY} : m_window->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}); - calcSize = calcSize.clamp(minSize, maxSize); + Vector2D maxSize = m_window->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}); + calcSize = calcSize.clamp(minSize, maxSize); calcPos += (availableSpace - calcSize) / 2.0; @@ -236,7 +241,7 @@ void CWindowTarget::updatePos(uint8_t flags) { calcPos.y = std::clamp(calcPos.y, MONITOR_WORKAREA.y, std::max(MONITOR_WORKAREA.y, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h - calcSize.y)); } - if (m_window->onSpecialWorkspace() && m_window && !Fullscreen::controller()->isFullscreen(m_window.lock())) { + if (m_window->onSpecialWorkspace() && m_window) { // if special, we adjust the coords a bit static auto PSCALEFACTOR = CConfigValue("dwindle:special_scale_factor"); diff --git a/src/managers/fullscreen/handler/FullscreenHandler.cpp b/src/managers/fullscreen/handler/FullscreenHandler.cpp index fef3881a7..a2255e950 100644 --- a/src/managers/fullscreen/handler/FullscreenHandler.cpp +++ b/src/managers/fullscreen/handler/FullscreenHandler.cpp @@ -38,7 +38,7 @@ bool IFullscreenHandler::isFullscreen(SP target, const std::opt !isFullscreen(target, std::nullopt, covering); } - // A window group's FS modes are considered to be owned by its current window + // A window group's FS state is considered to be owned by its current window if (const auto WINDOW_GROUP_TARGET = dc(target.get()); WINDOW_GROUP_TARGET && target->type() == Layout::TARGET_TYPE_GROUP) { if (WINDOW_GROUP_TARGET->getGroup() && WINDOW_GROUP_TARGET->getGroup()->current() && WINDOW_GROUP_TARGET->getGroup()->current()->m_target) target = WINDOW_GROUP_TARGET->getGroup()->current()->m_target; @@ -70,6 +70,7 @@ SFullscreenMode IFullscreenHandler::getFullscreenModes(SP targe if (!target) return {}; + // A window group's FS modes are considered to be owned by its current window if (const auto WINDOW_GROUP_TARGET = dc(target.get()); WINDOW_GROUP_TARGET && target->type() == Layout::TARGET_TYPE_GROUP) { if (WINDOW_GROUP_TARGET->getGroup() && WINDOW_GROUP_TARGET->getGroup()->current() && WINDOW_GROUP_TARGET->getGroup()->current()->m_target) target = WINDOW_GROUP_TARGET->getGroup()->current()->m_target; @@ -165,9 +166,19 @@ void IFullscreenHandler::updateTargetRulesAndDecos(const SP tar // Target must be a fullscreen window as considered by window/workspace rule matchers by now. // update all the values necessary for FS windows to get correct window dimensions and pos - WINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT | - Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE); - WINDOW->updateDecorationValues(); + + // If window is in a group, we need to update these values for ALL members of the group. + if (WINDOW->m_group) { + for (const auto& gm : WINDOW->m_group->windows()) { + gm->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT | + Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE); + gm->updateDecorationValues(); + } + } else { + WINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT | + Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE); + WINDOW->updateDecorationValues(); + } g_layoutManager->recalculateMonitor(MONITOR, Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN); getSpace()->recalculate(Layout::RECALCULATE_REASON_TOGGLE_DEFAULT_HANDLED_FULLSCREEN); } @@ -197,6 +208,7 @@ void IFullscreenHandler::setTargetSizeAndPosition(const SP targ Fullscreen::controller()->m_windowPosSettingQueued = true; LAYOUT_TARGET->setPositionGlobal(WORKAREA); } + Fullscreen::controller()->m_windowPosSettingQueued = false; } void IFullscreenHandler::syncTargetSizeAndPosition() { @@ -253,6 +265,7 @@ void IFullscreenHandler::syncTargetSizeAndPosition() { LAYOUT_TARGET->setPositionGlobal(WORKSPACE->m_space->workArea(FS_TARGET->floating())); } } + Fullscreen::controller()->m_windowPosSettingQueued = false; } void IFullscreenHandler::setNoMembersAboveFullscreen() {