diff options
| author | root <root@alpha.trunkmasters.com> | 2026-09-19 03:01:08 -0500 |
|---|---|---|
| committer | root <root@alpha.trunkmasters.com> | 2026-09-19 03:01:08 -0500 |
| commit | fc641028004011c6d8f1a4db9cd571823b825eab (patch) | |
| tree | bc02a7e2a487eb8c1f405156d0c44b2f377663f5 /gui-wm | |
| parent | 89051df130b5120fda29380e3b3b2e4effc349f8 (diff) | |
| download | baldeagleos-repo-fc641028004011c6d8f1a4db9cd571823b825eab.tar.gz baldeagleos-repo-fc641028004011c6d8f1a4db9cd571823b825eab.tar.xz baldeagleos-repo-fc641028004011c6d8f1a4db9cd571823b825eab.zip | |
Checkpoint dirty output before beosrepo update
Diffstat (limited to 'gui-wm')
87 files changed, 0 insertions, 46654 deletions
diff --git a/gui-wm/wayfire/Manifest b/gui-wm/wayfire/Manifest deleted file mode 100644 index d49e5bc528e2..000000000000 --- a/gui-wm/wayfire/Manifest +++ /dev/null @@ -1 +0,0 @@ -DIST wayfire-0.11.0.tar.xz 1045204 BLAKE2B 2f8a1a56c75cf39498d12f5cc2284e0c370b1def8745d8e356ac0a4032fd86ce0370b08af6d9f0f48836d740608359c60ab04e52c4f03c1e1c33c5991fbc23dd SHA512 192a4d6974fbee9e531ef349bd032233a162dfb5d2dbb302706b31f83d6eb5f57519e27e02fb3db4057c8b7d476fbaf0bc90a2cadd17628c5ef0133810dcb4e1 diff --git a/gui-wm/wayfire/files/0001-added-get_previous_layout-to-output-layout.patch b/gui-wm/wayfire/files/0001-added-get_previous_layout-to-output-layout.patch deleted file mode 100644 index cf5be3532228..000000000000 --- a/gui-wm/wayfire/files/0001-added-get_previous_layout-to-output-layout.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 221a39614392c1e107fe5da1b24837d33c5cdd97 Mon Sep 17 00:00:00 2001 -From: daniel <dvrlabs.tv@gmail.com> -Date: Tue, 13 Sep 2022 15:52:48 -0400 -Subject: [PATCH 1/3] added get_previous_layout to 'output-layout' - ---- - src/api/wayfire/output-layout.hpp | 7 +++++++ - src/core/output-layout.cpp | 14 ++++++++++++++ - 2 files changed, 21 insertions(+) - -diff --git a/src/api/wayfire/output-layout.hpp b/src/api/wayfire/output-layout.hpp -index fa6e83e3..cadbe9f4 100644 ---- a/src/api/wayfire/output-layout.hpp -+++ b/src/api/wayfire/output-layout.hpp -@@ -105,6 +105,13 @@ class output_layout_t : public wf::signal_provider_t - */ - wf::output_t *get_next_output(wf::output_t *output); - -+ /** -+ * @return the "previous" output in the layout. It is guaranteed that starting -+ * with any output in the layout, and successively calling this function -+ * will iterate over all outputs -+ */ -+ wf::output_t *get_previous_output(wf::output_t *output); -+ - /** - * @return the output_t associated with the wlr_output, or null if the - * output isn't found -diff --git a/src/core/output-layout.cpp b/src/core/output-layout.cpp -index 732bda82..e146fe66 100644 ---- a/src/core/output-layout.cpp -+++ b/src/core/output-layout.cpp -@@ -1577,6 +1577,20 @@ class output_layout_t::impl - } - } - -+ wf::output_t *get_previous_output(wf::output_t *output) -+ { -+ auto os = get_outputs(); -+ -+ auto it = std::find(os.rbegin(), os.rend(), output); -+ if ((it == os.rend()) || (std::next(it) == os.rend())) -+ { -+ return os[0]; -+ } else -+ { -+ return *(++it); -+ } -+ } -+ - wf::output_t *get_output_coords_at(const wf::pointf_t& origin, - wf::pointf_t& closest) - { --- -2.37.3 - diff --git a/gui-wm/wayfire/files/0002-Added-previous_output-and-previous_output_with_win-t.patch b/gui-wm/wayfire/files/0002-Added-previous_output-and-previous_output_with_win-t.patch deleted file mode 100644 index 78900b46336e..000000000000 --- a/gui-wm/wayfire/files/0002-Added-previous_output-and-previous_output_with_win-t.patch +++ /dev/null @@ -1,133 +0,0 @@ -From 933b40d5fa77ecbb19d984712ae5f051f72fdadf Mon Sep 17 00:00:00 2001 -From: daniel <dvrlabs.tv@gmail.com> -Date: Tue, 13 Sep 2022 19:10:27 -0400 -Subject: [PATCH 2/3] Added previous_output and previous_output_with_win to - 'oswitch' plugin - ---- - metadata/oswitch.xml | 10 ++++++ - plugins/single_plugins/oswitch.cpp | 50 ++++++++++++++++++++++++++++++ - src/core/output-layout.cpp | 5 +++ - 3 files changed, 65 insertions(+) - -diff --git a/metadata/oswitch.xml b/metadata/oswitch.xml -index 209ee0f0..c6779c77 100644 ---- a/metadata/oswitch.xml -+++ b/metadata/oswitch.xml -@@ -14,5 +14,15 @@ - <_long>Moves focus to the next output with the focused window with the specified activator.</_long> - <default><super> <shift> KEY_O</default> - </option> -+ <option name="previous_output" type="activator"> -+ <_short>previous output</_short> -+ <_long>Moves focus to the previous output with the specified activator.</_long> -+ <default><super> KEY_P</default> -+ </option> -+ <option name="previous_output_with_win" type="activator"> -+ <_short>previous output with window</_short> -+ <_long>Moves focus to the previous output with the focused window with the specified activator.</_long> -+ <default><super> <shift> KEY_P</default> -+ </option> - </plugin> - </wayfire> -diff --git a/plugins/single_plugins/oswitch.cpp b/plugins/single_plugins/oswitch.cpp -index 61b7ef2f..b7cb2f87 100644 ---- a/plugins/single_plugins/oswitch.cpp -+++ b/plugins/single_plugins/oswitch.cpp -@@ -23,6 +23,7 @@ class wayfire_output_manager : public wf::plugin_interface_t - return true; - }; - -+ - wf::activator_callback switch_output_with_window = [=] (auto) - { - auto next = -@@ -45,6 +46,46 @@ class wayfire_output_manager : public wf::plugin_interface_t - return true; - }; - -+ wf::wl_idle_call idle_previous_output; -+ -+ wf::activator_callback switch_output_previous = [=] (auto) -+ { -+ /* when we switch the output, the oswitch keybinding -+ * may be activated for the previous output, which we don't want, -+ * so we postpone the switch */ -+ auto previous = -+ wf::get_core().output_layout->get_previous_output(output); -+ idle_previous_output.run_once([=] () -+ { -+ wf::get_core().focus_output(previous); -+ }); -+ -+ return true; -+ }; -+ -+ -+ wf::activator_callback switch_output_with_window_previous = [=] (auto) -+ { -+ auto previous = -+ wf::get_core().output_layout->get_previous_output(output); -+ auto view = output->get_active_view(); -+ -+ if (!view) -+ { -+ switch_output_previous(wf::activator_data_t{}); -+ -+ return true; -+ } -+ -+ wf::get_core().move_view_to_output(view, previous, true); -+ idle_previous_output.run_once([=] () -+ { -+ wf::get_core().focus_output(previous); -+ }); -+ -+ return true; -+ }; -+ - public: - void init() - { -@@ -57,13 +98,22 @@ class wayfire_output_manager : public wf::plugin_interface_t - output->add_activator( - wf::option_wrapper_t<wf::activatorbinding_t>{"oswitch/next_output_with_win"}, - &switch_output_with_window); -+ output->add_activator( -+ wf::option_wrapper_t<wf::activatorbinding_t>{"oswitch/previous_output"}, -+ &switch_output_previous); -+ output->add_activator( -+ wf::option_wrapper_t<wf::activatorbinding_t>{"oswitch/previous_output_with_win"}, -+ &switch_output_with_window_previous); - } - - void fini() - { - output->rem_binding(&switch_output); - output->rem_binding(&switch_output_with_window); -+ output->rem_binding(&switch_output_previous); -+ output->rem_binding(&switch_output_with_window_previous); - idle_next_output.disconnect(); -+ idle_previous_output.disconnect(); - } - }; - -diff --git a/src/core/output-layout.cpp b/src/core/output-layout.cpp -index e146fe66..82455fec 100644 ---- a/src/core/output-layout.cpp -+++ b/src/core/output-layout.cpp -@@ -1670,6 +1670,11 @@ wf::output_t*output_layout_t::get_next_output(wf::output_t *output) - return pimpl->get_next_output(output); - } - -+wf::output_t*output_layout_t::get_previous_output(wf::output_t *output) -+{ -+ return pimpl->get_previous_output(output); -+} -+ - wf::output_t*output_layout_t::find_output(wlr_output *output) - { - return pimpl->find_output(output); --- -2.37.3 - diff --git a/gui-wm/wayfire/files/0003-prev-output-now-wraps-appropriately.patch b/gui-wm/wayfire/files/0003-prev-output-now-wraps-appropriately.patch deleted file mode 100644 index aa67133c20bc..000000000000 --- a/gui-wm/wayfire/files/0003-prev-output-now-wraps-appropriately.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 51a1ddac3352d9315ac53bf13cbbb5d76721c8ba Mon Sep 17 00:00:00 2001 -From: daniel <dvrlabs.tv@gmail.com> -Date: Tue, 13 Sep 2022 19:33:45 -0400 -Subject: [PATCH 3/3] prev output now wraps appropriately - ---- - src/core/output-layout.cpp | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/src/core/output-layout.cpp b/src/core/output-layout.cpp -index 82455fec..6d930971 100644 ---- a/src/core/output-layout.cpp -+++ b/src/core/output-layout.cpp -@@ -11,6 +11,7 @@ - #include "seat/cursor.hpp" - #include "core-impl.hpp" - -+#include <algorithm> - #include <xf86drmMode.h> - #include <sstream> - #include <cstring> -@@ -1580,15 +1581,17 @@ class output_layout_t::impl - wf::output_t *get_previous_output(wf::output_t *output) - { - auto os = get_outputs(); -+ std::reverse(os.begin(), os.end()); - -- auto it = std::find(os.rbegin(), os.rend(), output); -- if ((it == os.rend()) || (std::next(it) == os.rend())) -+ auto it = std::find(os.begin(), os.end(), output); -+ if ((it == os.end()) || (std::next(it) == os.end())) - { - return os[0]; - } else - { - return *(++it); - } -+ - } - - wf::output_t *get_output_coords_at(const wf::pointf_t& origin, --- -2.37.3 - diff --git a/gui-wm/wayfire/files/oswitch.patch b/gui-wm/wayfire/files/oswitch.patch deleted file mode 100644 index 5afbbf77ae8c..000000000000 --- a/gui-wm/wayfire/files/oswitch.patch +++ /dev/null @@ -1,233 +0,0 @@ -From 221a39614392c1e107fe5da1b24837d33c5cdd97 Mon Sep 17 00:00:00 2001 -From: daniel <dvrlabs.tv@gmail.com> -Date: Tue, 13 Sep 2022 15:52:48 -0400 -Subject: [PATCH 1/3] added get_previous_layout to 'output-layout' - ---- - src/api/wayfire/output-layout.hpp | 7 +++++++ - src/core/output-layout.cpp | 14 ++++++++++++++ - 2 files changed, 21 insertions(+) - -diff --git a/src/api/wayfire/output-layout.hpp b/src/api/wayfire/output-layout.hpp -index fa6e83e3..cadbe9f4 100644 ---- a/src/api/wayfire/output-layout.hpp -+++ b/src/api/wayfire/output-layout.hpp -@@ -105,6 +105,13 @@ class output_layout_t : public wf::signal_provider_t - */ - wf::output_t *get_next_output(wf::output_t *output); - -+ /** -+ * @return the "previous" output in the layout. It is guaranteed that starting -+ * with any output in the layout, and successively calling this function -+ * will iterate over all outputs -+ */ -+ wf::output_t *get_previous_output(wf::output_t *output); -+ - /** - * @return the output_t associated with the wlr_output, or null if the - * output isn't found -diff --git a/src/core/output-layout.cpp b/src/core/output-layout.cpp -index 732bda82..e146fe66 100644 ---- a/src/core/output-layout.cpp -+++ b/src/core/output-layout.cpp -@@ -1577,6 +1577,20 @@ class output_layout_t::impl - } - } - -+ wf::output_t *get_previous_output(wf::output_t *output) -+ { -+ auto os = get_outputs(); -+ -+ auto it = std::find(os.rbegin(), os.rend(), output); -+ if ((it == os.rend()) || (std::next(it) == os.rend())) -+ { -+ return os[0]; -+ } else -+ { -+ return *(++it); -+ } -+ } -+ - wf::output_t *get_output_coords_at(const wf::pointf_t& origin, - wf::pointf_t& closest) - { --- -2.37.3 - -From 933b40d5fa77ecbb19d984712ae5f051f72fdadf Mon Sep 17 00:00:00 2001 -From: daniel <dvrlabs.tv@gmail.com> -Date: Tue, 13 Sep 2022 19:10:27 -0400 -Subject: [PATCH 2/3] Added previous_output and previous_output_with_win to - 'oswitch' plugin - ---- - metadata/oswitch.xml | 10 ++++++ - plugins/single_plugins/oswitch.cpp | 50 ++++++++++++++++++++++++++++++ - src/core/output-layout.cpp | 5 +++ - 3 files changed, 65 insertions(+) - -diff --git a/metadata/oswitch.xml b/metadata/oswitch.xml -index 209ee0f0..c6779c77 100644 ---- a/metadata/oswitch.xml -+++ b/metadata/oswitch.xml -@@ -14,5 +14,15 @@ - <_long>Moves focus to the next output with the focused window with the specified activator.</_long> - <default><super> <shift> KEY_O</default> - </option> -+ <option name="previous_output" type="activator"> -+ <_short>previous output</_short> -+ <_long>Moves focus to the previous output with the specified activator.</_long> -+ <default><super> KEY_P</default> -+ </option> -+ <option name="previous_output_with_win" type="activator"> -+ <_short>previous output with window</_short> -+ <_long>Moves focus to the previous output with the focused window with the specified activator.</_long> -+ <default><super> <shift> KEY_P</default> -+ </option> - </plugin> - </wayfire> -diff --git a/plugins/single_plugins/oswitch.cpp b/plugins/single_plugins/oswitch.cpp -index 61b7ef2f..b7cb2f87 100644 ---- a/plugins/single_plugins/oswitch.cpp -+++ b/plugins/single_plugins/oswitch.cpp -@@ -23,6 +23,7 @@ class wayfire_output_manager : public wf::plugin_interface_t - return true; - }; - -+ - wf::activator_callback switch_output_with_window = [=] (auto) - { - auto next = -@@ -45,6 +46,46 @@ class wayfire_output_manager : public wf::plugin_interface_t - return true; - }; - -+ wf::wl_idle_call idle_previous_output; -+ -+ wf::activator_callback switch_output_previous = [=] (auto) -+ { -+ /* when we switch the output, the oswitch keybinding -+ * may be activated for the previous output, which we don't want, -+ * so we postpone the switch */ -+ auto previous = -+ wf::get_core().output_layout->get_previous_output(output); -+ idle_previous_output.run_once([=] () -+ { -+ wf::get_core().focus_output(previous); -+ }); -+ -+ return true; -+ }; -+ -+ -+ wf::activator_callback switch_output_with_window_previous = [=] (auto) -+ { -+ auto previous = -+ wf::get_core().output_layout->get_previous_output(output); -+ auto view = output->get_active_view(); -+ -+ if (!view) -+ { -+ switch_output_previous(wf::activator_data_t{}); -+ -+ return true; -+ } -+ -+ wf::get_core().move_view_to_output(view, previous, true); -+ idle_previous_output.run_once([=] () -+ { -+ wf::get_core().focus_output(previous); -+ }); -+ -+ return true; -+ }; -+ - public: - void init() - { -@@ -57,13 +98,22 @@ class wayfire_output_manager : public wf::plugin_interface_t - output->add_activator( - wf::option_wrapper_t<wf::activatorbinding_t>{"oswitch/next_output_with_win"}, - &switch_output_with_window); -+ output->add_activator( -+ wf::option_wrapper_t<wf::activatorbinding_t>{"oswitch/previous_output"}, -+ &switch_output_previous); -+ output->add_activator( -+ wf::option_wrapper_t<wf::activatorbinding_t>{"oswitch/previous_output_with_win"}, -+ &switch_output_with_window_previous); - } - - void fini() - { - output->rem_binding(&switch_output); - output->rem_binding(&switch_output_with_window); -+ output->rem_binding(&switch_output_previous); -+ output->rem_binding(&switch_output_with_window_previous); - idle_next_output.disconnect(); -+ idle_previous_output.disconnect(); - } - }; - -diff --git a/src/core/output-layout.cpp b/src/core/output-layout.cpp -index e146fe66..82455fec 100644 ---- a/src/core/output-layout.cpp -+++ b/src/core/output-layout.cpp -@@ -1670,6 +1670,11 @@ wf::output_t*output_layout_t::get_next_output(wf::output_t *output) - return pimpl->get_next_output(output); - } - -+wf::output_t*output_layout_t::get_previous_output(wf::output_t *output) -+{ -+ return pimpl->get_previous_output(output); -+} -+ - wf::output_t*output_layout_t::find_output(wlr_output *output) - { - return pimpl->find_output(output); --- -2.37.3 - -From 51a1ddac3352d9315ac53bf13cbbb5d76721c8ba Mon Sep 17 00:00:00 2001 -From: daniel <dvrlabs.tv@gmail.com> -Date: Tue, 13 Sep 2022 19:33:45 -0400 -Subject: [PATCH 3/3] prev output now wraps appropriately - ---- - src/core/output-layout.cpp | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/src/core/output-layout.cpp b/src/core/output-layout.cpp -index 82455fec..6d930971 100644 ---- a/src/core/output-layout.cpp -+++ b/src/core/output-layout.cpp -@@ -11,6 +11,7 @@ - #include "seat/cursor.hpp" - #include "core-impl.hpp" - -+#include <algorithm> - #include <xf86drmMode.h> - #include <sstream> - #include <cstring> -@@ -1580,15 +1581,17 @@ class output_layout_t::impl - wf::output_t *get_previous_output(wf::output_t *output) - { - auto os = get_outputs(); -+ std::reverse(os.begin(), os.end()); - -- auto it = std::find(os.rbegin(), os.rend(), output); -- if ((it == os.rend()) || (std::next(it) == os.rend())) -+ auto it = std::find(os.begin(), os.end(), output); -+ if ((it == os.end()) || (std::next(it) == os.end())) - { - return os[0]; - } else - { - return *(++it); - } -+ - } - - wf::output_t *get_output_coords_at(const wf::pointf_t& origin, --- -2.37.3 - diff --git a/gui-wm/wayfire/files/regex.patch b/gui-wm/wayfire/files/regex.patch deleted file mode 100644 index 179b61c886b1..000000000000 --- a/gui-wm/wayfire/files/regex.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/subprojects/wf-utils/wayfire/lexer/literal.cpp b/subprojects/wf-utils/wayfire/lexer/literal.cpp -index cf4c9cf..5e8a042 100644 ---- a/subprojects/wf-utils/wayfire/lexer/literal.cpp -+++ b/subprojects/wf-utils/wayfire/lexer/literal.cpp -@@ -3,6 +3,7 @@ - #include <ctype.h> - #include <stdexcept> - #include <string> -+#include <bits/stdc++.h> - - namespace wf - { -@@ -86,7 +87,7 @@ variant_t parse_literal(const std::string &s) - } - - // Deal with float or double here. -- if (s.find('.') != std::string::npos) -+ if (std::count(std::begin(s), std::end(s), '.') == 1) - { - // Float or Double. - if ((s.find('f') != std::string::npos) || (s.find('F') != std::string::npos)) diff --git a/gui-wm/wayfire/files/wayfire-0.10.0-fix-musl.patch b/gui-wm/wayfire/files/wayfire-0.10.0-fix-musl.patch deleted file mode 100644 index d7398049ca6e..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.0-fix-musl.patch +++ /dev/null @@ -1,28 +0,0 @@ -https://github.com/WayfireWM/wayfire/pull/2875/commits/4040e432f917fb488477193304142553e0df8413 -https://bugs.gentoo.org/966059 - -From: Sammy <samexner@protonmail.com> -Date: Sun, 2 Nov 2025 18:45:01 -0700 -Subject: [PATCH] fix musl compilation - -changed uint to standard type unsigned int, fixes compilation error on musl ---- a/src/json.cpp -+++ b/src/json.cpp -@@ -225,7 +225,7 @@ json_reference_t& json_reference_t::operator =(const int& v) - return *this; - } - --json_reference_t& json_reference_t::operator =(const uint& v) -+json_reference_t& json_reference_t::operator =(const unsigned int& v) - { - yyjson_mut_set_uint(this->v, v); - return *this; -@@ -331,7 +331,7 @@ bool json_reference_t::is_uint() const - (yyjson_mut_get_uint(v) <= std::numeric_limits<unsigned int>::max()); - } - --json_reference_t::operator uint() const -+json_reference_t::operator unsigned int() const - { - wf::dassert(is_uint()); - return yyjson_mut_get_uint(v); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube-animation-notes.md b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube-animation-notes.md deleted file mode 100644 index fe5753db5db8..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube-animation-notes.md +++ /dev/null @@ -1,69 +0,0 @@ -# Transparent cube animation compatibility notes - -## Goal - -Keep cube rendering active all the time, while normal Wayfire animations still work. - -Observed state: - -- Cube full-time render works. -- Wobbly works at the present moment. -- Zoom was visible on `bemenu`. -- Alacritty did not visibly zoom because `animate/duration` was set to `0ms`. - -## Working theory - -This does not look like a global "only one animation at a time" problem. - -Wayfire already allows multiple normal animation hooks. The problem is more specific: - -- Cube renders workspaces through `workspace_stream_node_t`. -- Cube's `cube_render_instance_t` builds child render instances for each workspace once, when the cube render instance is created. -- `workspace_stream_node_t::workspace_stream_instance_t` also builds its child render instances once, when the stream render instance is created. -- Fire and zoom are animate-plugin view transformers added during map/unmap. -- If those transformers are added after cube has cached the workspace render instances, cube can keep rendering the old child render path and never include the new fire/zoom transformer. -- Wobbly can still work because it is already represented in the rendered path, or because its persistent transformer/render instance is present when cube captures the workspace stream. - -## Minimum target - -Do not rewrite the plugin capability system first. - -The minimum useful change may still involve workspace stream behavior, but the event-driven refresh experiment was not justified by the later Alacritty finding. - -Likely places: - -- `src/output/workspace-stream.cpp` -- `plugins/cube/cube.cpp` - -Candidate approaches: - -1. Add scene-update tracking to `workspace_stream_node_t::workspace_stream_instance_t`. - - Listen for root scene updates while the stream instance exists. - - Regenerate the stream's internal child render instances when child lists, enabled state, or transformer-affecting geometry changes. - - This would make cube pick up fire/zoom transformers without deactivating cube. - - Removed for now: this was likely overkill and did not explain the Alacritty case. - -2. Add a cube-local rebuild hook. - - When cube is active, listen for scene updates on the output. - - Rebuild `cube_render_node_t` when relevant view transformer structure changes. - - This is less general but may be smaller for this patch. - -3. Heavy fallback for testing only. - - Rebuild cube workspace stream instances every frame. - - This is useful to confirm the theory, but probably too expensive and may interfere with render-instance-owned hooks. - -## Current Direction - -Do not keep the workspace-stream refresh experiment unless a corrected animation config still proves that cube hides normal animate transformers. - -Next test: - -- Set `animate/duration` to a visible value such as `500ms circle`. -- Re-test Alacritty open/close animation with cube full-time render active. -- If it still fails while bemenu works, then investigate tiling/container handling rather than broad stream regeneration. - -## Avoid for now - -- Broadly allowing conflicting plugin capabilities. -- Forcing cube to deactivate after each vswitch animation. -- Rebuilding the entire compositor animation model. diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch deleted file mode 100644 index 6006eee7f283..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch +++ /dev/null @@ -1,1370 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,19 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> -+ <option name="window_move_alpha" type="double"> -+ <_short>Window move alpha</_short> -+ <_long>Opacity used for the focused window while it is being held or moved between workspaces.</_long> -+ <default>0.65</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -21,6 +21,14 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -52,6 +64,16 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,15 +1,22 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" -+#include <wlr/types/wlr_keyboard.h> - - #include <glm/gtc/matrix_transform.hpp> - #include <wayfire/img.hpp> -@@ -39,6 +46,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +85,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +101,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +147,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +173,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +156,10 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; - workspaces.push_back(node); - } - } -@@ -176,10 +247,24 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ wayfire_toplevel_view post_move_dim_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "workspace-move-window-dim"; -+ bool fixed_overlay_added_transformer = false; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ wf::option_wrapper_t<double> window_move_alpha{"vswitch/window_move_alpha"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; -+ bool cube_render_paused = false; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +315,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +337,17 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); -+ output->connect(&on_cube_render_pause); -+ output->connect(&on_fixed_view_disappeared); -+ output->connect(&on_view_geometry_changed); -+ wf::get_core().connect(&on_fixed_view_set_output); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - wf::gles::run_in_context([&] - { - load_program(); - }); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +450,266 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ if (fixed_view && (fixed_view->get_output() != output)) -+ { -+ fixed_view = nullptr; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ wf::signal::connection_t<cube_render_pause_signal> -+ on_cube_render_pause = [=] (cube_render_pause_signal *ev) -+ { -+ pause_cube_render(ev->pause); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void pause_cube_render(bool pause) -+ { -+ if (pause) -+ { -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = nullptr; -+ cube_render_paused = true; -+ output->render->damage_whole(); -+ } -+ -+ return; -+ } -+ -+ if (cube_render_paused && !render_node) -+ { -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ cube_render_paused = false; -+ output->render->damage_whole(); -+ } -+ } -+ -+ void remove_held_dim_transformers(wayfire_toplevel_view view) -+ { -+ if (!view) -+ { -+ return; -+ } -+ -+ auto node = view->get_transformed_node(); -+ while (node->get_transformer(held_dim_transformer_name)) -+ { -+ node->rem_transformer(held_dim_transformer_name); -+ } -+ } -+ -+ bool has_held_dim_transformer(wayfire_toplevel_view view) -+ { -+ if (!view) -+ { -+ return false; -+ } -+ -+ return view->get_transformed_node()->get_transformer( -+ held_dim_transformer_name) != nullptr; -+ } -+ -+ void clear_fixed_workspace_overlay(bool keep_post_move_dim = true) -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ if (keep_post_move_dim && -+ ((wf::get_core().seat->get_keyboard_modifiers() & -+ held_window_modifiers) == held_window_modifiers)) -+ { -+ post_move_dim_view = fixed_overlay_view; -+ fixed_overlay_view = nullptr; -+ return; -+ } else -+ { -+ if (fixed_overlay_added_transformer) -+ { -+ remove_held_dim_transformers(fixed_overlay_view); -+ } -+ -+ post_move_dim_view = nullptr; -+ } -+ -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ fixed_overlay_added_transformer = false; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void clear_post_move_window_dim() -+ { -+ if (!post_move_dim_view) -+ { -+ return; -+ } -+ -+ remove_held_dim_transformers(post_move_dim_view); -+ wf::scene::set_node_enabled( -+ post_move_dim_view->get_transformed_node(), true); -+ if (fixed_overlay_node) -+ { -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ post_move_dim_view = nullptr; -+ fixed_overlay_added_transformer = false; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_post_move_window_dim(); -+ clear_fixed_workspace_overlay(false); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ fixed_overlay_added_transformer = false; -+ if (!has_held_dim_transformer(view)) -+ { -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = wf::clamp((double)window_move_alpha, 0.0, 1.0); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ fixed_overlay_added_transformer = true; -+ } -+ -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +652,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +694,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +738,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +794,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +822,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; -+ -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); - -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +960,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +985,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +999,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +1022,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - -@@ -751,8 +1106,83 @@ - output->render->schedule_redraw(); - } - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) != held_window_modifiers) -+ { -+ clear_post_move_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> -+ on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!post_move_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_window_modifiers) -+ { -+ clear_post_move_window_dim(); -+ } -+ }; -+ -+ void clear_if_fixed_view(wayfire_view view) -+ { -+ if ((view == fixed_overlay_view) || (view == post_move_dim_view) || -+ (view == fixed_workspace_view)) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_post_move_window_dim(); -+ clear_fixed_workspace_overlay(false); -+ rebuild_cube_render_node(); -+ output->render->schedule_redraw(); -+ } -+ } -+ -+ wf::signal::connection_t<wf::view_disappeared_signal> -+ on_fixed_view_disappeared = [=] (wf::view_disappeared_signal *ev) -+ { -+ clear_if_fixed_view(ev->view); -+ }; -+ -+ wf::signal::connection_t<wf::view_set_output_signal> -+ on_fixed_view_set_output = [=] (wf::view_set_output_signal *ev) -+ { -+ if ((ev->view == fixed_overlay_view) || (ev->view == post_move_dim_view) || -+ (ev->view == fixed_workspace_view)) -+ { -+ if (ev->view->get_output() != output) -+ { -+ clear_if_fixed_view(ev->view); -+ } -+ } -+ }; -+ -+ wf::signal::connection_t<wf::view_geometry_changed_signal> -+ on_view_geometry_changed = [=] (wf::view_geometry_changed_signal *ev) -+ { -+ (void)ev; -+ if (!fixed_workspace_view || !render_node) -+ { -+ return; -+ } -+ -+ rebuild_cube_render_node(); -+ output->render->schedule_redraw(); -+ }; -+ - void fini() override - { -+ clear_post_move_window_dim(); - if (output->is_plugin_active(grab_interface.name)) - { - deactivate(); ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,18 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ -+struct cube_render_pause_signal -+{ -+ bool pause = false; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,13 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ std::shared_ptr<wf::scene::translation_node_t> held_dim_overlay_node; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "workspace-move-window-dim"; -+ wf::option_wrapper_t<double> window_move_alpha{"vswitch/window_move_alpha"}; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +382,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +394,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -411,7 +416,12 @@ - { - if (delta == wf::point_t{0, 0}) - { -- // Consume input event -+ if (view) -+ { -+ show_held_window_dim(view); -+ } -+ -+ // Consume input event - return true; - } - -@@ -446,6 +456,91 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void remove_held_dim_transformers(wayfire_toplevel_view view) -+ { -+ if (!view) -+ { -+ return; -+ } -+ -+ auto node = view->get_transformed_node(); -+ while (node->get_transformer(held_dim_transformer_name)) -+ { -+ node->rem_transformer(held_dim_transformer_name); -+ } -+ } -+ -+ void clear_held_window_dim() -+ { -+ if (held_dim_view) -+ { -+ remove_held_dim_transformers(held_dim_view); -+ wf::scene::set_node_enabled(held_dim_view->get_transformed_node(), true); -+ } -+ -+ if (held_dim_overlay_node) -+ { -+ wf::scene::remove_child(held_dim_overlay_node); -+ held_dim_overlay_node = nullptr; -+ } -+ -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ wayfire_toplevel_view get_held_window_target_view() -+ { -+ auto view = find_topmost_parent( -+ toplevel_cast(wf::get_core().seat->get_active_view())); -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL)) -+ { -+ return nullptr; -+ } -+ -+ return view; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = wf::clamp((double)window_move_alpha, 0.0, 1.0); -+ remove_held_dim_transformers(view); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ -+ auto overlay = std::make_shared<wf::vswitch::vswitch_overlay_node_t>(view); -+ held_dim_overlay_node = -+ std::make_shared<wf::scene::translation_node_t>(); -+ held_dim_overlay_node->set_children_list({overlay}); -+ held_dim_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), held_dim_overlay_node); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(held_dim_overlay_node, -+ held_dim_overlay_node->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +555,62 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped() && -+ (view->get_output() == output)) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ clear_held_window_dim(); -+ wf::vswitch::move_view(fixed_view, target_ws); -+ output->wset()->set_workspace(target_ws); -+ wf::get_core().seat->focus_view(view); -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ -+ output->wset()->set_workspace(target_ws); -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = nullptr; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +619,47 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ clear_held_window_dim(); -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ show_held_window_dim(get_held_window_target_view()); -+ } else if (held_dim_view) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -520,6 +665,15 @@ - - return; - } -+ -+ if (!ev->fixed_views.empty()) -+ { -+ cube_workspace_animation_signal cube_ev; -+ cube_ev.delta = ev->new_viewport - ev->old_viewport; -+ cube_ev.anchor_workspace = ev->old_viewport; -+ cube_ev.fixed_view = ev->fixed_views[0]; -+ output->emit(&cube_ev); -+ } - - if (is_active()) - { -@@ -556,6 +706,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.bad-unified-overlay-hold-20260605-060044 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.bad-unified-overlay-hold-20260605-060044 deleted file mode 100644 index 255748526abd..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.bad-unified-overlay-hold-20260605-060044 +++ /dev/null @@ -1,1206 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,20 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr float moving_window_alpha = 0.90f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +310,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +331,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +443,161 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +611,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +653,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +697,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +753,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +781,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +919,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +944,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +958,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +981,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,13 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ std::shared_ptr<wf::vswitch::vswitch_overlay_node_t> held_dim_overlay_node; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +382,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +394,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +418,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +456,62 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (held_dim_view) -+ { -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ wf::scene::set_node_enabled(held_dim_view->get_transformed_node(), true); -+ } -+ -+ if (held_dim_overlay_node) -+ { -+ wf::scene::remove_child(held_dim_overlay_node); -+ held_dim_overlay_node = nullptr; -+ } -+ -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ -+ held_dim_overlay_node = -+ std::make_shared<wf::vswitch::vswitch_overlay_node_t>(view); -+ wf::scene::add_front(wf::get_core().scene(), held_dim_overlay_node); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(held_dim_overlay_node, -+ held_dim_overlay_node->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +552,56 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ clear_held_window_dim(); -+ -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +616,49 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ clear_held_window_dim(); -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +703,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.no-face-shading-20260605-044923 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.no-face-shading-20260605-044923 deleted file mode 100644 index c6479e61aedb..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.no-face-shading-20260605-044923 +++ /dev/null @@ -1,928 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,16 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +306,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +327,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +439,146 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +592,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +634,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +678,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +734,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +762,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +900,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +925,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +939,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +962,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,10 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,7 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -486,6 +487,44 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) -+ { -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ auto fixed_view = toplevel_cast(view); -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ } -+ - if (!is_active() && !start_switch()) - { - return false; diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-all-window-alpha-080-output-local-hold-20260605-061915 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-all-window-alpha-080-output-local-hold-20260605-061915 deleted file mode 100644 index b282359f1d46..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-all-window-alpha-080-output-local-hold-20260605-061915 +++ /dev/null @@ -1,1209 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,20 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr float moving_window_alpha = 0.80f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +310,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +331,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +443,161 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +611,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +653,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +697,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +753,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +781,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +919,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +944,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +958,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +981,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,13 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ std::shared_ptr<wf::scene::translation_node_t> held_dim_overlay_node; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.80f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +382,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +394,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +418,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +456,65 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (held_dim_view) -+ { -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ wf::scene::set_node_enabled(held_dim_view->get_transformed_node(), true); -+ } -+ -+ if (held_dim_overlay_node) -+ { -+ wf::scene::remove_child(held_dim_overlay_node); -+ held_dim_overlay_node = nullptr; -+ } -+ -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ -+ auto overlay = std::make_shared<wf::vswitch::vswitch_overlay_node_t>(view); -+ held_dim_overlay_node = -+ std::make_shared<wf::scene::translation_node_t>(); -+ held_dim_overlay_node->set_children_list({overlay}); -+ held_dim_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), held_dim_overlay_node); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(held_dim_overlay_node, -+ held_dim_overlay_node->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +555,56 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ clear_held_window_dim(); -+ -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +619,49 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ clear_held_window_dim(); -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +706,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-anchor-backup-20260605-043727 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-anchor-backup-20260605-043727 deleted file mode 100644 index 1dcf4aa7e4e5..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-anchor-backup-20260605-043727 +++ /dev/null @@ -1,878 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -4,10 +4,14 @@ - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +43,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +82,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +98,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +144,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +170,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +207,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +244,15 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +304,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +325,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +437,143 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +587,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,10 +629,10 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -+ fixed_workspace_view = nullptr; -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); - output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); - - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); -@@ -457,13 +671,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +727,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +755,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +893,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +918,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +932,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +955,15 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -160,7 +160,10 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * colorFactor * cube_brightness, -+ tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,11 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,7 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -486,6 +487,26 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto fixed_view = toplevel_cast(view); -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ cube_workspace_animation_signal ev; -+ ev.delta = delta; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ } -+ - if (!is_active() && !start_switch()) - { - return false; diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-cube-overlay-disappear-cleanup-20260605-081256 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-cube-overlay-disappear-cleanup-20260605-081256 deleted file mode 100644 index 577c9d01261c..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-cube-overlay-disappear-cleanup-20260605-081256 +++ /dev/null @@ -1,1290 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,15 +1,22 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" -+#include <wlr/types/wlr_keyboard.h> - - #include <glm/gtc/matrix_transform.hpp> - #include <wayfire/img.hpp> -@@ -39,6 +46,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +85,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +101,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +147,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +173,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +210,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +247,23 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ wayfire_toplevel_view post_move_dim_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.80f; -+ static constexpr float moving_window_alpha = 0.80f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +315,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +336,21 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +450,195 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ if ((wf::get_core().seat->get_keyboard_modifiers() & -+ held_window_modifiers) == held_window_modifiers) -+ { -+ post_move_dim_view = fixed_overlay_view; -+ fixed_overlay_view = nullptr; -+ return; -+ } else -+ { -+ fixed_overlay_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ post_move_dim_view = nullptr; -+ } -+ -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void clear_post_move_window_dim() -+ { -+ if (!post_move_dim_view) -+ { -+ return; -+ } -+ -+ post_move_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ wf::scene::set_node_enabled( -+ post_move_dim_view->get_transformed_node(), true); -+ if (fixed_overlay_node) -+ { -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ post_move_dim_view = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_post_move_window_dim(); -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +652,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +694,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +738,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +794,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +822,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; -+ -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); - -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +960,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +985,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +999,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +1022,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - -@@ -751,8 +1106,37 @@ - output->render->schedule_redraw(); - } - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) != held_window_modifiers) -+ { -+ clear_post_move_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> -+ on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!post_move_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_window_modifiers) -+ { -+ clear_post_move_window_dim(); -+ } -+ }; -+ - void fini() override - { -+ clear_post_move_window_dim(); - if (output->is_plugin_active(grab_interface.name)) - { - deactivate(); ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,13 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ std::shared_ptr<wf::scene::translation_node_t> held_dim_overlay_node; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.80f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +382,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +394,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +418,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +456,65 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (held_dim_view) -+ { -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ wf::scene::set_node_enabled(held_dim_view->get_transformed_node(), true); -+ } -+ -+ if (held_dim_overlay_node) -+ { -+ wf::scene::remove_child(held_dim_overlay_node); -+ held_dim_overlay_node = nullptr; -+ } -+ -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ -+ auto overlay = std::make_shared<wf::vswitch::vswitch_overlay_node_t>(view); -+ held_dim_overlay_node = -+ std::make_shared<wf::scene::translation_node_t>(); -+ held_dim_overlay_node->set_children_list({overlay}); -+ held_dim_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), held_dim_overlay_node); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(held_dim_overlay_node, -+ held_dim_overlay_node->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +555,56 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ clear_held_window_dim(); -+ -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +619,49 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ clear_held_window_dim(); -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +706,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-cube-overlay-removes-alpha-on-clear-20260605-062209 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-cube-overlay-removes-alpha-on-clear-20260605-062209 deleted file mode 100644 index f4f7fc3c4072..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-cube-overlay-removes-alpha-on-clear-20260605-062209 +++ /dev/null @@ -1,1209 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,20 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.80f; -+ static constexpr float moving_window_alpha = 0.80f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +310,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +331,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +443,161 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +611,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +653,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +697,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +753,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +781,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +919,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +944,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +958,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +981,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,13 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ std::shared_ptr<wf::scene::translation_node_t> held_dim_overlay_node; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.80f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +382,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +394,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +418,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +456,65 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (held_dim_view) -+ { -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ wf::scene::set_node_enabled(held_dim_view->get_transformed_node(), true); -+ } -+ -+ if (held_dim_overlay_node) -+ { -+ wf::scene::remove_child(held_dim_overlay_node); -+ held_dim_overlay_node = nullptr; -+ } -+ -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ -+ auto overlay = std::make_shared<wf::vswitch::vswitch_overlay_node_t>(view); -+ held_dim_overlay_node = -+ std::make_shared<wf::scene::translation_node_t>(); -+ held_dim_overlay_node->set_children_list({overlay}); -+ held_dim_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), held_dim_overlay_node); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(held_dim_overlay_node, -+ held_dim_overlay_node->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +555,56 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ clear_held_window_dim(); -+ -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +619,49 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ clear_held_window_dim(); -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +706,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-cube-reuse-held-dim-20260605-052028 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-cube-reuse-held-dim-20260605-052028 deleted file mode 100644 index f83517138cb4..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-cube-reuse-held-dim-20260605-052028 +++ /dev/null @@ -1,1135 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,16 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +306,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +327,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +439,146 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +592,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +634,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +678,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +734,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +762,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +900,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +925,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +939,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +962,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -380,6 +384,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +397,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +409,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +433,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +471,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +556,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +618,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +706,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-delta-normalize-backup-20260605-043931 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-delta-normalize-backup-20260605-043931 deleted file mode 100644 index e33b7adad4b5..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-delta-normalize-backup-20260605-043931 +++ /dev/null @@ -1,898 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,16 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +306,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +327,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +439,146 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +592,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,16 +634,18 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); - output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); - - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -@@ -457,13 +678,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +734,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +762,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +900,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +925,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +939,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +962,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -160,7 +160,10 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * colorFactor * cube_brightness, -+ tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,7 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -486,6 +487,27 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto fixed_view = toplevel_cast(view); -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ cube_workspace_animation_signal ev; -+ ev.delta = delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ } -+ - if (!is_active() && !start_switch()) - { - return false; diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-direct-modifier-dim-20260605-050918 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-direct-modifier-dim-20260605-050918 deleted file mode 100644 index 7740f0c0eb85..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-direct-modifier-dim-20260605-050918 +++ /dev/null @@ -1,1123 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,18 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ const std::string fixed_overlay_transformer_name = "cube-fixed-overlay"; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +308,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +329,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +441,159 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (fixed_overlay_view) -+ { -+ fixed_overlay_view->get_transformed_node()->rem_transformer( -+ fixed_overlay_transformer_name); -+ fixed_overlay_view = nullptr; -+ } -+ -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = 0.65f; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, fixed_overlay_transformer_name); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +607,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +649,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +693,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +749,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +777,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +915,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +940,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +954,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +977,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,8 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -380,6 +382,10 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +393,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +405,14 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +428,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +466,45 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +545,55 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ clear_held_window_dim(); -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +608,33 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +679,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-disable-fixed-view-normal-node-20260605-052720 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-disable-fixed-view-normal-node-20260605-052720 deleted file mode 100644 index bb4bf1253ce0..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-disable-fixed-view-normal-node-20260605-052720 +++ /dev/null @@ -1,1182 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,18 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +308,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +329,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +441,152 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +600,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +642,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +686,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +742,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +770,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +908,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +933,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +947,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +970,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-disable-vswitch-overlay-alpha-20260605-052329 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-disable-vswitch-overlay-alpha-20260605-052329 deleted file mode 100644 index 40bbcf29c551..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-disable-vswitch-overlay-alpha-20260605-052329 +++ /dev/null @@ -1,1143 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,18 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +308,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +329,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +441,152 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +600,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +642,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +686,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +742,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +770,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +908,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +933,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +947,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +970,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -380,6 +384,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +397,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +409,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +433,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +471,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +556,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +618,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +706,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-empty-vswitch-cube-20260605-045314 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-empty-vswitch-cube-20260605-045314 deleted file mode 100644 index 498bd663a461..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-empty-vswitch-cube-20260605-045314 +++ /dev/null @@ -1,928 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,16 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +306,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +327,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +439,146 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +592,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +634,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +678,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +734,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +762,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +900,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +925,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +939,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +962,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,7 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -486,6 +487,44 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) -+ { -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ auto fixed_view = toplevel_cast(view); -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ } -+ - if (!is_active() && !start_switch()) - { - return false; diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-hold-070-moving-090-20260605-060729 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-hold-070-moving-090-20260605-060729 deleted file mode 100644 index 86f63743c40d..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-hold-070-moving-090-20260605-060729 +++ /dev/null @@ -1,1193 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,20 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr float moving_window_alpha = 0.70f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +310,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +331,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +443,161 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +611,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +653,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +697,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +753,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +781,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +919,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +944,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +958,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +981,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-hold-opacity-20260605-050310 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-hold-opacity-20260605-050310 deleted file mode 100644 index ffda9fa3803f..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-hold-opacity-20260605-050310 +++ /dev/null @@ -1,956 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,18 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ const std::string fixed_overlay_transformer_name = "cube-fixed-overlay"; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +308,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +329,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +441,159 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (fixed_overlay_view) -+ { -+ fixed_overlay_view->get_transformed_node()->rem_transformer( -+ fixed_overlay_transformer_name); -+ fixed_overlay_view = nullptr; -+ } -+ -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = 0.5f; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, fixed_overlay_transformer_name); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +607,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +649,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +693,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +749,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +777,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +915,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +940,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +954,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +977,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,7 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -486,12 +487,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-initial-080-moving-080-post-090-20260605-061218 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-initial-080-moving-080-post-090-20260605-061218 deleted file mode 100644 index 104d1cadfe84..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-initial-080-moving-080-post-090-20260605-061218 +++ /dev/null @@ -1,1193 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,20 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr float moving_window_alpha = 0.90f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +310,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +331,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +443,161 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +611,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +653,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +697,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +753,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +781,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +919,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +944,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +958,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +981,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.70f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-keep-dim-during-arrow-20260605-051459 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-keep-dim-during-arrow-20260605-051459 deleted file mode 100644 index 94dee13337c4..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-keep-dim-during-arrow-20260605-051459 +++ /dev/null @@ -1,1136 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,16 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +306,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +327,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +439,146 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +592,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +634,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +678,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +734,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +762,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +900,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +925,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +939,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +962,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -380,6 +384,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +397,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +409,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +433,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +471,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +556,55 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ clear_held_window_dim(); -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +619,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +707,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-keep-post-move-overlay-20260605-063142 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-keep-post-move-overlay-20260605-063142 deleted file mode 100644 index f98fd2e61a18..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-keep-post-move-overlay-20260605-063142 +++ /dev/null @@ -1,1279 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,15 +1,22 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" -+#include <wlr/types/wlr_keyboard.h> - - #include <glm/gtc/matrix_transform.hpp> - #include <wayfire/img.hpp> -@@ -39,6 +46,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +85,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +101,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +147,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +173,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +210,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +247,23 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ wayfire_toplevel_view post_move_dim_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.80f; -+ static constexpr float moving_window_alpha = 0.80f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +315,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +336,21 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +450,184 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ if ((wf::get_core().seat->get_keyboard_modifiers() & -+ held_window_modifiers) == held_window_modifiers) -+ { -+ post_move_dim_view = fixed_overlay_view; -+ } else -+ { -+ fixed_overlay_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ post_move_dim_view = nullptr; -+ } -+ -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void clear_post_move_window_dim() -+ { -+ if (!post_move_dim_view) -+ { -+ return; -+ } -+ -+ post_move_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ post_move_dim_view = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +641,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +683,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +727,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +783,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +811,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } - -+ current_rotation = 0; -+ -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +949,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +974,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +988,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +1011,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - -@@ -751,8 +1095,37 @@ - output->render->schedule_redraw(); - } - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) != held_window_modifiers) -+ { -+ clear_post_move_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> -+ on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!post_move_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_window_modifiers) -+ { -+ clear_post_move_window_dim(); -+ } -+ }; -+ - void fini() override - { -+ clear_post_move_window_dim(); - if (output->is_plugin_active(grab_interface.name)) - { - deactivate(); ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,13 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ std::shared_ptr<wf::scene::translation_node_t> held_dim_overlay_node; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.80f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +382,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +394,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +418,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +456,65 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (held_dim_view) -+ { -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ wf::scene::set_node_enabled(held_dim_view->get_transformed_node(), true); -+ } -+ -+ if (held_dim_overlay_node) -+ { -+ wf::scene::remove_child(held_dim_overlay_node); -+ held_dim_overlay_node = nullptr; -+ } -+ -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ -+ auto overlay = std::make_shared<wf::vswitch::vswitch_overlay_node_t>(view); -+ held_dim_overlay_node = -+ std::make_shared<wf::scene::translation_node_t>(); -+ held_dim_overlay_node->set_children_list({overlay}); -+ held_dim_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), held_dim_overlay_node); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(held_dim_overlay_node, -+ held_dim_overlay_node->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +555,56 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ clear_held_window_dim(); -+ -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +619,49 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ clear_held_window_dim(); -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +706,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-lighting-backup-20260605-044635 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-lighting-backup-20260605-044635 deleted file mode 100644 index b511f67cf8d3..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-lighting-backup-20260605-044635 +++ /dev/null @@ -1,915 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,16 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +306,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +327,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +439,146 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +592,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,16 +634,18 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); - output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); - - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -@@ -457,13 +678,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +734,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +762,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +900,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +925,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +939,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +962,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -160,7 +160,10 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * colorFactor * cube_brightness, -+ tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,7 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -486,6 +487,44 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) -+ { -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ auto fixed_view = toplevel_cast(view); -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ } -+ - if (!is_active() && !start_switch()) - { - return false; diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-move-window-opacity-20260605-045816 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-move-window-opacity-20260605-045816 deleted file mode 100644 index c6b90097acf5..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-move-window-opacity-20260605-045816 +++ /dev/null @@ -1,941 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,16 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +306,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +327,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +439,146 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +592,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +634,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +678,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +734,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +762,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +900,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +925,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +939,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +962,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,7 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -486,12 +487,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-moving-alpha-070-hold-090-20260605-060354 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-moving-alpha-070-hold-090-20260605-060354 deleted file mode 100644 index 6714be42d793..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-moving-alpha-070-hold-090-20260605-060354 +++ /dev/null @@ -1,1193 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,20 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr float moving_window_alpha = 0.80f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +310,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +331,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +443,161 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +611,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +653,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +697,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +753,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +781,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +919,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +944,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +958,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +981,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-moving-alpha-080-hold-090-20260605-060155 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-moving-alpha-080-hold-090-20260605-060155 deleted file mode 100644 index bb35d8bdf4b5..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-moving-alpha-080-hold-090-20260605-060155 +++ /dev/null @@ -1,1193 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,20 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr float moving_window_alpha = 0.90f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +310,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +331,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +443,161 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +611,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +653,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +697,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +753,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +781,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +919,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +944,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +958,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +981,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-moving-window-alpha-040-20260605-052919 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-moving-window-alpha-040-20260605-052919 deleted file mode 100644 index e1b060880c8c..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-moving-window-alpha-040-20260605-052919 +++ /dev/null @@ -1,1192 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,19 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +309,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +330,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +442,161 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +610,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +652,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +696,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +752,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +780,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +918,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +943,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +957,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +980,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-output-local-hold-overlay-20260605-061701 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-output-local-hold-overlay-20260605-061701 deleted file mode 100644 index 3e85d8187b29..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-output-local-hold-overlay-20260605-061701 +++ /dev/null @@ -1,1193 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,20 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr float moving_window_alpha = 0.80f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +310,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +331,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +443,161 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +611,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +653,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +697,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +753,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +781,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +919,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +944,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +958,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +981,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.80f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-overlay-backup-20260605-043031 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-overlay-backup-20260605-043031 deleted file mode 100644 index 03653ec0016b..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-overlay-backup-20260605-043031 +++ /dev/null @@ -1,750 +0,0 @@ ---- a/metadata/cube.xml 2025-12-03 07:47:41.000000000 -0600 -+++ b/metadata/cube.xml 2026-06-05 01:33:45.138068266 -0500 -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/src/api/wayfire/workspace-stream.hpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/src/api/wayfire/workspace-stream.hpp 2026-06-05 02:15:00.000000000 -0500 -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -22,6 +24,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/src/output/workspace-stream.cpp 2026-06-05 02:15:00.000000000 -0500 -@@ -3,6 +3,7 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> -@@ -44,6 +45,48 @@ - auto view = node_to_view(ch); - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); -+ -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } - - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. ---- a/plugins/cube/cube.cpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/cube.cpp 2026-06-05 01:33:36.309088942 -0500 -@@ -4,6 +4,8 @@ - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> -@@ -51,10 +51,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -63,6 +66,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -101,6 +119,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -113,6 +140,7 @@ -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -145,7 +172,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -180,6 +214,10 @@ - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -266,6 +301,6 @@ - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -282,10 +313,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -390,6 +434,116 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ fixed_workspace_view = fixed_view; -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -406,8 +466,6 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); -@@ -415,8 +473,5 @@ - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -449,9 +504,7 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -+ release_cube_input(); - output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); - - /* Figure out how much we have rotated and switch workspace */ -@@ -490,6 +543,6 @@ - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -@@ -497,5 +550,28 @@ - * and reset other attribs */ -- reset_attribs(); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - -@@ -522,6 +582,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -544,19 +612,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; -+ -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); - - animation.cube_animation.start(); -- - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +652,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +677,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +693,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -716,7 +771,15 @@ - { - output->render->schedule_redraw(); - } else if (animation.in_exit) -- { -+ { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; ---- a/plugins/cube/cubemap.cpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/cubemap.cpp 2026-06-05 01:33:01.832169666 -0500 -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +148,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/cubemap.hpp 2026-06-05 01:32:51.699193389 -0500 -@@ -14,6 +14,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/shaders-3-2.tpp 2026-06-05 01:33:16.853134497 -0500 -@@ -160,7 +160,10 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * colorFactor * cube_brightness, -+ tex.a); - })"; ---- a/plugins/cube/shaders.tpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/shaders.tpp 2026-06-05 01:33:11.415147230 -0500 -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/cube-control-signal.hpp 2026-06-05 04:45:00.000000000 -0500 -@@ -1,6 +1,8 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube -@@ -18,4 +20,11 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/skydome.cpp 2026-06-05 01:32:44.626209951 -0500 -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +155,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +176,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/skydome.hpp 2026-06-05 01:32:28.896246795 -0500 -@@ -18,6 +18,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/vswitch.cpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/vswitch/vswitch.cpp 2026-06-05 04:10:00.000000000 -0500 -@@ -10,6 +10,7 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -487,6 +488,22 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ auto target_ws = output->wset()->get_current_workspace() + delta; -+ auto fixed_view = toplevel_cast(view); -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ cube_workspace_animation_signal ev; -+ ev.delta = delta; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ } -+ - if (!is_active() && !start_switch()) - { - return false; diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-perceptual-moving-alpha-040-20260605-053905 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-perceptual-moving-alpha-040-20260605-053905 deleted file mode 100644 index ee946e1a9f49..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-perceptual-moving-alpha-040-20260605-053905 +++ /dev/null @@ -1,1182 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,18 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.50f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +308,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +329,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +441,152 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +600,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +642,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +686,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +742,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +770,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +908,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +933,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +947,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +970,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.50f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-post-move-dim-owned-by-cube-20260605-062651 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-post-move-dim-owned-by-cube-20260605-062651 deleted file mode 100644 index 3ef032ce326d..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-post-move-dim-owned-by-cube-20260605-062651 +++ /dev/null @@ -1,1211 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,20 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.80f; -+ static constexpr float moving_window_alpha = 0.80f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +310,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +331,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +443,163 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ fixed_overlay_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +613,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +655,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +699,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +755,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +783,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +921,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +946,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +960,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +983,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,13 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ std::shared_ptr<wf::scene::translation_node_t> held_dim_overlay_node; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.80f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +382,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +394,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +418,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +456,65 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (held_dim_view) -+ { -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ wf::scene::set_node_enabled(held_dim_view->get_transformed_node(), true); -+ } -+ -+ if (held_dim_overlay_node) -+ { -+ wf::scene::remove_child(held_dim_overlay_node); -+ held_dim_overlay_node = nullptr; -+ } -+ -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ -+ auto overlay = std::make_shared<wf::vswitch::vswitch_overlay_node_t>(view); -+ held_dim_overlay_node = -+ std::make_shared<wf::scene::translation_node_t>(); -+ held_dim_overlay_node->set_children_list({overlay}); -+ held_dim_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), held_dim_overlay_node); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(held_dim_overlay_node, -+ held_dim_overlay_node->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +555,56 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ clear_held_window_dim(); -+ -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +619,49 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ clear_held_window_dim(); -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +706,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-remove-cube-overlay-dim-20260605-051151 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-remove-cube-overlay-dim-20260605-051151 deleted file mode 100644 index f0ec284255f0..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-remove-cube-overlay-dim-20260605-051151 +++ /dev/null @@ -1,1151 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,18 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ const std::string fixed_overlay_transformer_name = "cube-fixed-overlay"; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +308,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +329,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +441,159 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (fixed_overlay_view) -+ { -+ fixed_overlay_view->get_transformed_node()->rem_transformer( -+ fixed_overlay_transformer_name); -+ fixed_overlay_view = nullptr; -+ } -+ -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = 0.65f; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, fixed_overlay_transformer_name); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +607,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +649,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +693,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +749,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +777,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +915,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +940,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +954,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +977,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -380,6 +384,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +397,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +409,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +433,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +471,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +556,55 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ clear_held_window_dim(); -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +619,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +707,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-revert-disappear-cleanup-20260605-084947 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-revert-disappear-cleanup-20260605-084947 deleted file mode 100644 index 0dd8338b80dc..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-revert-disappear-cleanup-20260605-084947 +++ /dev/null @@ -1,1330 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,15 +1,22 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" -+#include <wlr/types/wlr_keyboard.h> - - #include <glm/gtc/matrix_transform.hpp> - #include <wayfire/img.hpp> -@@ -39,6 +46,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +85,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +101,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +147,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +173,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +210,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +247,23 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ wayfire_toplevel_view post_move_dim_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.80f; -+ static constexpr float moving_window_alpha = 0.80f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +315,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +336,22 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); -+ output->connect(&on_fixed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +451,224 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void drop_fixed_workspace_overlay() -+ { -+ if (post_move_dim_view) -+ { -+ post_move_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ wf::scene::set_node_enabled( -+ post_move_dim_view->get_transformed_node(), true); -+ post_move_dim_view = nullptr; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ fixed_overlay_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ if (fixed_overlay_node) -+ { -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ output->render->schedule_redraw(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ if ((wf::get_core().seat->get_keyboard_modifiers() & -+ held_window_modifiers) == held_window_modifiers) -+ { -+ post_move_dim_view = fixed_overlay_view; -+ fixed_overlay_view = nullptr; -+ return; -+ } else -+ { -+ fixed_overlay_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ post_move_dim_view = nullptr; -+ } -+ -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void clear_post_move_window_dim() -+ { -+ if (!post_move_dim_view) -+ { -+ return; -+ } -+ -+ post_move_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ wf::scene::set_node_enabled( -+ post_move_dim_view->get_transformed_node(), true); -+ if (fixed_overlay_node) -+ { -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ post_move_dim_view = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_post_move_window_dim(); -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +682,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +724,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +768,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +824,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +852,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; -+ -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); - -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +990,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +1015,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +1029,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +1052,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - -@@ -751,8 +1136,47 @@ - output->render->schedule_redraw(); - } - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) != held_window_modifiers) -+ { -+ clear_post_move_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> -+ on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!post_move_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_window_modifiers) -+ { -+ clear_post_move_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::view_disappeared_signal> on_fixed_view_disappear = -+ [=] (wf::view_disappeared_signal *ev) -+ { -+ if ((ev->view == fixed_overlay_view) || (ev->view == post_move_dim_view)) -+ { -+ drop_fixed_workspace_overlay(); -+ } -+ }; -+ - void fini() override - { -+ drop_fixed_workspace_overlay(); -+ clear_post_move_window_dim(); - if (output->is_plugin_active(grab_interface.name)) - { - deactivate(); ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,13 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ std::shared_ptr<wf::scene::translation_node_t> held_dim_overlay_node; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.80f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +382,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +394,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +418,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +456,65 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (held_dim_view) -+ { -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ wf::scene::set_node_enabled(held_dim_view->get_transformed_node(), true); -+ } -+ -+ if (held_dim_overlay_node) -+ { -+ wf::scene::remove_child(held_dim_overlay_node); -+ held_dim_overlay_node = nullptr; -+ } -+ -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ -+ auto overlay = std::make_shared<wf::vswitch::vswitch_overlay_node_t>(view); -+ held_dim_overlay_node = -+ std::make_shared<wf::scene::translation_node_t>(); -+ held_dim_overlay_node->set_children_list({overlay}); -+ held_dim_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), held_dim_overlay_node); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(held_dim_overlay_node, -+ held_dim_overlay_node->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +555,56 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ clear_held_window_dim(); -+ -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +619,49 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ clear_held_window_dim(); -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +706,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-unified-overlay-hold-20260605-055439 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-unified-overlay-hold-20260605-055439 deleted file mode 100644 index bb35d8bdf4b5..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-unified-overlay-hold-20260605-055439 +++ /dev/null @@ -1,1193 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,20 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr float moving_window_alpha = 0.90f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +310,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +331,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +443,161 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +611,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +653,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +697,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +753,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +781,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +919,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +944,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +958,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +981,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.90f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-unified-window-alpha-050-20260605-053432 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-unified-window-alpha-050-20260605-053432 deleted file mode 100644 index 584ce1499562..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-unified-window-alpha-050-20260605-053432 +++ /dev/null @@ -1,1183 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,19 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; -+ static constexpr float moving_window_alpha = 0.40f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +309,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +330,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +442,152 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +601,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +643,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +687,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +743,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +771,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +909,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +934,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +948,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +971,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-vswitch-modifier-binding-20260605-050558 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-vswitch-modifier-binding-20260605-050558 deleted file mode 100644 index cb0eee8efa74..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-vswitch-modifier-binding-20260605-050558 +++ /dev/null @@ -1,1098 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,18 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ const std::string fixed_overlay_transformer_name = "cube-fixed-overlay"; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +308,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +329,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +441,159 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (fixed_overlay_view) -+ { -+ fixed_overlay_view->get_transformed_node()->rem_transformer( -+ fixed_overlay_transformer_name); -+ fixed_overlay_view = nullptr; -+ } -+ -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = 0.65f; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, fixed_overlay_transformer_name); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +607,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +649,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +693,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +749,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +777,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +915,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +940,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +954,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +977,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,8 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -380,6 +382,10 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.65f; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +393,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +405,14 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +428,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +466,45 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +545,55 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ clear_held_window_dim(); -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +608,33 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +679,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-window-alpha-030-20260605-054356 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-window-alpha-030-20260605-054356 deleted file mode 100644 index f216376f0597..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-window-alpha-030-20260605-054356 +++ /dev/null @@ -1,1183 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,19 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.50f; -+ static constexpr float moving_window_alpha = 0.40f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +309,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +330,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +442,152 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +601,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +643,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +687,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +743,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +771,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +909,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +934,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +948,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +971,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.50f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-window-alpha-070-single-overlay-20260605-054805 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-window-alpha-070-single-overlay-20260605-054805 deleted file mode 100644 index 4bffb39ad59d..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-window-alpha-070-single-overlay-20260605-054805 +++ /dev/null @@ -1,1183 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,19 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.30f; -+ static constexpr float moving_window_alpha = 0.30f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +309,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +330,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +442,152 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +601,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +643,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +687,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +743,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +771,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +909,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +934,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +948,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +971,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.30f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-window-alpha-090-20260605-055009 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-window-alpha-090-20260605-055009 deleted file mode 100644 index c991ece9ff2a..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-window-alpha-090-20260605-055009 +++ /dev/null @@ -1,1193 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/metadata/vswitch.xml -+++ b/metadata/vswitch.xml -@@ -54,6 +54,11 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -1,13 +1,18 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +44,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +83,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +99,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +145,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +171,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +208,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +245,20 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.70f; -+ static constexpr float moving_window_alpha = 0.70f; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +310,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +331,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +443,161 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = moving_window_alpha; -+ view->get_transformed_node()->rem_transformer(held_dim_transformer_name); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +611,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,19 +653,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -457,13 +697,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +753,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +781,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +919,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +944,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +958,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +981,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,12 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,12 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "vswitch-held-window-dim"; -+ static constexpr float held_window_alpha = 0.70f; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +381,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +393,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +417,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (!only_view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +455,51 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void clear_held_window_dim() -+ { -+ if (!held_dim_view) -+ { -+ return; -+ } -+ -+ held_dim_view->get_transformed_node()->rem_transformer( -+ held_dim_transformer_name); -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = held_window_alpha; -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(view->get_transformed_node(), -+ view->get_transformed_node()->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +540,54 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; -+ } -+ -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ } else -+ { -+ output->wset()->set_workspace(target_ws); -+ } -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +602,50 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ auto active = wf::get_core().seat->get_active_view(); -+ auto view = find_topmost_parent(toplevel_cast(active)); -+ show_held_window_dim(view); -+ } else -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -556,6 +690,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-wrap-backup-20260605-043446 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-wrap-backup-20260605-043446 deleted file mode 100644 index 9d6c3d85534b..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-wrap-backup-20260605-043446 +++ /dev/null @@ -1,874 +0,0 @@ ---- a/metadata/cube.xml -+++ b/metadata/cube.xml -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/src/api/wayfire/workspace-stream.hpp -+++ b/src/api/wayfire/workspace-stream.hpp -@@ -5,6 +5,8 @@ - #include <wayfire/render.hpp> - #include <wayfire/object.hpp> - #include <wayfire/region.hpp> -+#include <wayfire/toplevel-view.hpp> -+#include <vector> - - namespace wf - { -@@ -21,6 +23,17 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ -+ // Toplevel views which should be left out of the workspace stream. -+ std::vector<wayfire_toplevel_view> excluded_views; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -3,7 +3,9 @@ - #include "wayfire/region.hpp" - #include "wayfire/scene-render.hpp" - #include "wayfire/scene.hpp" -+#include "wayfire/toplevel-view.hpp" - #include "wayfire/view.hpp" -+#include "wayfire/view-transform.hpp" - #include <wayfire/config/types.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/output.hpp> -@@ -42,6 +44,25 @@ - push_damage(damage); - }; - -+ std::vector<wf::scene::node_ptr> disabled_excluded_nodes; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ auto node = tree_view->get_transformed_node(); -+ if (node && node->is_enabled()) -+ { -+ node->set_enabled(false); -+ disabled_excluded_nodes.push_back(node); -+ } -+ } -+ } -+ - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { - for (auto& ch : output_node->get_children()) -@@ -52,6 +73,48 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ -+ auto toplevel = toplevel_cast(view); -+ if (toplevel) -+ { -+ bool excluded = false; -+ for (auto excluded_view : self->excluded_views) -+ { -+ if (!excluded_view) -+ { -+ continue; -+ } -+ -+ for (auto tree_view : excluded_view->enumerate_views(false)) -+ { -+ if (tree_view == toplevel) -+ { -+ excluded = true; -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ break; -+ } -+ } -+ -+ if (excluded) -+ { -+ continue; -+ } -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -@@ -68,6 +131,11 @@ - - wf::dassert(instances.size() == is_desktop_environment.size(), "Setting de flag is wrong!"); - } -+ -+ for (auto& node : disabled_excluded_nodes) -+ { -+ node->set_enabled(true); -+ } - } - - void schedule_instructions( ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -4,10 +4,14 @@ - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" - -@@ -39,6 +43,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,10 +82,11 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -61,6 +98,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,6 +144,16 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -110,9 +170,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -146,7 +207,14 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; -+ if (cube->fixed_workspace_view) -+ { -+ node->excluded_views.push_back(cube->fixed_workspace_view); -+ } - workspaces.push_back(node); - } - } -@@ -176,10 +244,15 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -231,7 +304,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -252,10 +325,19 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); - wf::gles::run_in_context([&] - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -355,6 +437,143 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void clear_fixed_workspace_overlay() -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_fixed_workspace_overlay(); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -368,16 +587,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -415,10 +629,10 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -+ fixed_workspace_view = nullptr; -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); - output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); - - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); -@@ -457,13 +671,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -490,6 +727,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -510,19 +755,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +893,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +918,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +932,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -677,6 +955,15 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - ---- a/plugins/cube/cubemap.cpp -+++ b/plugins/cube/cubemap.cpp -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp -+++ b/plugins/cube/cubemap.hpp -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp -+++ b/plugins/cube/shaders-3-2.tpp -@@ -160,7 +160,10 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * colorFactor * cube_brightness, -+ tex.a); - })"; ---- a/plugins/cube/shaders.tpp -+++ b/plugins/cube/shaders.tpp -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/cube-control-signal.hpp -+++ b/plugins/cube/cube-control-signal.hpp -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,11 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ ---- a/plugins/cube/skydome.cpp -+++ b/plugins/cube/skydome.cpp -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp -+++ b/plugins/cube/skydome.hpp -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); ---- a/plugins/vswitch/vswitch.cpp -+++ b/plugins/vswitch/vswitch.cpp -@@ -10,6 +10,7 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -486,6 +487,22 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped()) -+ { -+ auto target_ws = output->wset()->get_current_workspace() + delta; -+ auto fixed_view = toplevel_cast(view); -+ if (fixed_view) -+ { -+ output->wset()->set_workspace(target_ws, {fixed_view}); -+ wf::get_core().seat->focus_view(view); -+ cube_workspace_animation_signal ev; -+ ev.delta = delta; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ } -+ - if (!is_active() && !start_switch()) - { - return false; diff --git a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.working-backup-20260605-035448 b/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.working-backup-20260605-035448 deleted file mode 100644 index fa4f486d943f..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.working-backup-20260605-035448 +++ /dev/null @@ -1,634 +0,0 @@ ---- a/metadata/cube.xml 2025-12-03 07:47:41.000000000 -0600 -+++ b/metadata/cube.xml 2026-06-05 01:33:45.138068266 -0500 -@@ -49,6 +49,30 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="opacity" type="double"> -+ <_short>Cube opacity</_short> -+ <_long>Sets the opacity of the cube faces.</_long> -+ <default>1.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Turns the activated cube slightly while idle so adjacent workspaces remain visible.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> ---- a/plugins/cube/cube-background.hpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/cube-background.hpp 2026-06-05 01:32:23.015260588 -0500 -@@ -9,6 +9,12 @@ - public: - virtual void render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) = 0; -+ virtual void render_output_background(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs) -+ { -+ render_frame(fb, attribs); -+ } -+ - virtual ~wf_cube_background_base() = default; - }; - ---- a/src/api/wayfire/workspace-stream.hpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/src/api/wayfire/workspace-stream.hpp 2026-06-05 02:15:00.000000000 -0500 -@@ -22,6 +22,14 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ - wf::output_t*const output; - const wf::point_t ws; - ---- a/src/output/workspace-stream.cpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/src/output/workspace-stream.cpp 2026-06-05 02:15:00.000000000 -0500 -@@ -44,6 +44,16 @@ - auto view = node_to_view(ch); - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); -+ -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } - - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. ---- a/plugins/cube/cube.cpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/cube.cpp 2026-06-05 01:33:36.309088942 -0500 -@@ -4,6 +4,8 @@ - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> -@@ -51,10 +51,12 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; -+ wf::region_t output_background_damage; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::region_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -- - wf::signal::connection_t<wf::scene::node_damage_signal> on_cube_damage = - [=] (wf::scene::node_damage_signal *ev) - { -@@ -63,6 +66,21 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::region_t& damage) -+ { -+ output_background_damage |= damage; -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ output_background_damage |= output_background->get_bounding_box(); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -101,6 +119,18 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::region_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ -+ output_background_damage.clear(); -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; -@@ -113,6 +140,7 @@ -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -145,7 +172,10 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; - workspaces.push_back(node); - } - } -@@ -180,6 +210,11 @@ - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_opacity{"cube/opacity"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ bool input_is_grabbed = false; -+ bool persistent_mouse_cube = false; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -266,6 +301,6 @@ - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -286,6 +317,14 @@ - { - load_program(); - }); -+ -+ activate(); -+ animation.in_exit = false; -+ animation.cube_animation.rotation.set( -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5), -+ animation.side_angle * wf::clamp((double)resting_yaw, 0.0, 0.5)); -+ update_view_matrix(); -+ output->render->schedule_redraw(); - } - - void handle_pointer_button(const wlr_pointer_button_event& event) override -@@ -390,6 +424,62 @@ - output->render->schedule_redraw(); - } - -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -406,8 +466,6 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); -@@ -415,8 +473,5 @@ - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -449,9 +504,7 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -+ release_cube_input(); - output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); - - /* Figure out how much we have rotated and switch workspace */ -@@ -490,6 +543,6 @@ - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -@@ -497,5 +550,28 @@ - * and reset other attribs */ -- reset_attribs(); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - -@@ -522,6 +582,15 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ persistent_mouse_cube = false; -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -544,19 +612,48 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; -+ persistent_mouse_cube = true; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; -+ -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); - - animation.cube_animation.start(); -- - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -620,7 +652,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -645,6 +677,10 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_opacity", -+ wf::clamp((double)face_opacity, 0.0, 1.0)); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -657,10 +693,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -716,7 +755,9 @@ - { - output->render->schedule_redraw(); - } else if (animation.in_exit) -- { -+ { - deactivate(); -+ } else { -+ output->render->schedule_redraw(); - } - }; ---- a/plugins/cube/cubemap.cpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/cubemap.cpp 2026-06-05 01:33:01.832169666 -0500 -@@ -84,6 +84,18 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_output_background(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs) -+{ -+ render_frame(fb, attribs, false); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +148,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; ---- a/plugins/cube/cubemap.hpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/cubemap.hpp 2026-06-05 01:32:51.699193389 -0500 -@@ -9,10 +9,14 @@ - wf_cube_background_cubemap(); - virtual void render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) override; -+ virtual void render_output_background(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs) override; - - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - ---- a/plugins/cube/shaders-3-2.tpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/shaders-3-2.tpp 2026-06-05 01:33:16.853134497 -0500 -@@ -160,7 +160,11 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_opacity; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * colorFactor * cube_brightness, -+ tex.a); - })"; ---- a/plugins/cube/shaders.tpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/shaders.tpp 2026-06-05 01:33:11.415147230 -0500 -@@ -19,7 +19,10 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_opacity; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; ---- a/plugins/cube/skydome.cpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/skydome.cpp 2026-06-05 01:32:44.626209951 -0500 -@@ -129,6 +129,18 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_output_background(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs) -+{ -+ render_frame(fb, attribs, false); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +155,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +176,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - ---- a/plugins/cube/skydome.hpp 2025-12-03 07:47:41.000000000 -0600 -+++ b/plugins/cube/skydome.hpp 2026-06-05 01:32:28.896246795 -0500 -@@ -11,12 +11,16 @@ - wf_cube_background_skydome(wf::output_t *output); - virtual void render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) override; -+ virtual void render_output_background(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs) override; - - virtual ~wf_cube_background_skydome(); - - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); diff --git a/gui-wm/wayfire/files/wayfire-0.11.0-cube-damage-stacking.patch b/gui-wm/wayfire/files/wayfire-0.11.0-cube-damage-stacking.patch deleted file mode 100644 index 4f471cf68005..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.11.0-cube-damage-stacking.patch +++ /dev/null @@ -1,188 +0,0 @@ ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -145,14 +145,23 @@ - std::vector<wf::scene::render_instruction_t>& instructions, - const wf::render_target_t& target, wf::regionf_t& damage) override - { -+ auto bbox = self->get_bounding_box(); -+ auto our_damage = damage & bbox; -+ if (our_damage.empty()) -+ { -+ return; -+ } -+ -+ // The cube renders in output-local coordinates, including its -+ // damage. Never repaint pixels occluded by a higher layer. -+ our_damage += -wf::origin(bbox); - instructions.push_back(wf::scene::render_instruction_t{ - .instance = this, -- .target = target.translated(-wf::origin(self->get_bounding_box())), -- .damage = damage & self->get_bounding_box(), -+ .target = target.translated(-wf::origin(bbox)), -+ .damage = our_damage, - }); - -- wf::regionf_t background_damage = -- output_background->get_bounding_box(); -+ wf::regionf_t background_damage = our_damage; - auto background_target = - target.translated(-wf::origin(self->get_bounding_box())); - for (auto& instance : output_background_instances) -@@ -161,15 +170,22 @@ - instructions, background_target, background_damage); - } - -- auto bbox = self->get_bounding_box(); -- - damage ^= bbox; - - for (int i = 0; i < (int)ws_instances.size(); i++) - { - const float scale = self->cube->output->handle->scale; - auto bbox = self->workspaces[i]->get_bounding_box(); -- framebuffers[i].allocate(wf::dimensions(bbox), scale); -+ if (framebuffers[i].allocate(wf::dimensions(bbox), scale) == -+ wf::buffer_reallocation_result_t::REALLOCATED) -+ { -+ ws_damage[i] |= bbox; -+ } -+ -+ if (ws_damage[i].empty()) -+ { -+ continue; -+ } - - wf::render_target_t target{framebuffers[i]}; - target.geometry = self->workspaces[i]->get_bounding_box(); -@@ -177,14 +193,14 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = self->workspaces[i]->get_bounding_box(); -+ params.damage = ws_damage[i]; - params.reference_output = self->cube->output; - params.target = target; - params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - -+ ws_damage[i].clear(); - wf::render_pass_t::run(params); -- ws_damage[i].clear(); - } - } - -@@ -532,11 +548,12 @@ - input_is_grabbed = false; - } - -- void add_cube_render_node() -+ void add_desktop_render_node(const wf::scene::node_ptr& node, -+ const wf::scene::node_ptr& before = nullptr) - { - auto root = wf::get_core().scene(); - auto children = root->get_children(); -- auto overlay_layer = -+ auto anchor = before ? before : - root->layers[(int)wf::scene::layer::OVERLAY]; - - std::vector<wf::scene::node_ptr> updated; -@@ -544,9 +561,9 @@ - bool inserted = false; - for (auto& child : children) - { -- if (!inserted && (child == overlay_layer)) -- { -- updated.push_back(render_node); -+ if (!inserted && (child == anchor)) -+ { -+ updated.push_back(node); - inserted = true; - } - -@@ -555,11 +572,18 @@ - - if (!inserted) - { -- updated.insert(updated.begin(), render_node); -+ // The built-in overlay layer is always present. Do not place -+ // desktop content above screensavers or locks if that changes. -+ updated.push_back(node); - } - - root->set_children_list(updated); - wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void add_cube_render_node() -+ { -+ add_desktop_render_node(render_node); - } - - void rebuild_cube_render_node() -@@ -705,7 +729,7 @@ - fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); - fixed_overlay_node->set_children_list({overlay}); - fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -- wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ add_desktop_render_node(fixed_overlay_node, render_node); - } - - /* Tries to initialize renderer, activate plugin, etc. */ -@@ -1027,7 +1051,6 @@ - load_program(); - } - -- GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); - /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); -@@ -1070,8 +1093,12 @@ - GL_CALL(glEnable(GL_BLEND)); - GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); -- render_cube(GL_CCW, buffers); -- render_cube(GL_CW, buffers); -+ wf::gles::for_each_scissor_rect(data.target, data.damage, [&] -+ { -+ GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -+ render_cube(GL_CCW, buffers); -+ render_cube(GL_CW, buffers); -+ }); - GL_CALL(glDisable(GL_CULL_FACE)); - GL_CALL(glDisable(GL_BLEND)); - -@@ -1080,13 +1107,21 @@ - }); - } - -+ bool animation_was_running = false; - wf::effect_hook_t pre_hook = [=] () - { - update_view_matrix(); -- wf::scene::damage_node(render_node, render_node->get_bounding_box()); -- if (animation.cube_animation.running()) -- { -- output->render->damage_whole(); -+ const bool running = animation.cube_animation.running(); -+ if (render_node && (running || animation_was_running)) -+ { -+ // Repaint moving geometry and its final settled frame. At rest, -+ // workspace/background damage already schedules the needed frames. -+ wf::scene::damage_node(render_node, render_node->get_bounding_box()); -+ } -+ -+ animation_was_running = running; -+ if (running) -+ { - output->render->schedule_redraw(); - } else if (animation.in_exit) - { -@@ -1099,8 +1134,6 @@ - clear_fixed_workspace_overlay(); - rebuild_cube_render_node(); - } -- -- output->render->schedule_redraw(); - } - }; - diff --git a/gui-wm/wayfire/files/wayfire-0.11.0-cube-rendering-notes.md b/gui-wm/wayfire/files/wayfire-0.11.0-cube-rendering-notes.md deleted file mode 100644 index 335592634e18..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.11.0-cube-rendering-notes.md +++ /dev/null @@ -1,30 +0,0 @@ -# Cube damage and layer ordering, revision 3 - -The revision 2 clearing fix remains in place. Revision 3 removes the continuous -redraw measures that previously concealed damage errors: - -- Reuse cube-face textures and repaint their accumulated damage. Newly allocated - textures still receive a full repaint. -- Stop scheduling frames when the cube animation finishes. Child damage drives - updates at rest; an extra final animation frame paints the settled geometry. -- Remove duplicate whole-output damage during animation. - -The cube now follows Wayfire's render-instruction contract. It skips empty -damage, translates damage into the same output-local coordinates as its render -target, limits background rendering to that damage, and scissors the cube's GL -draws and depth clear. Previously the background and cube could overwrite pixels -that were not scheduled to be repainted, including higher-layer contents. - -Held-window overlays are inserted above the cube but below SCREENSAVER and LOCK. -Previously `add_front(scene_root, overlay)` put them above every normal layer. -The desktop sampler can now include those overlays in the desktop composition. - -Scene rebuilds for held-window transitions, and whole-output damage when -activating/deactivating or pausing/resuming the cube, remain necessary. This is -not a claim that all full-output repaints can be removed: moving a 3D scene can -change any pixel on the output. - -No public API or ABI changes; the subslot remains `0/0.11.screensaver1`. -Oledsaver 1.0.13 already requests the correct screensaver layer and needs no -renderer changes for this patch. See `tests/render-regression/README.md` for -isolated, reproducible checks. diff --git a/gui-wm/wayfire/files/wayfire-0.11.0-desktop-sampling.patch b/gui-wm/wayfire/files/wayfire-0.11.0-desktop-sampling.patch deleted file mode 100644 index f3dacad5d2fd..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.11.0-desktop-sampling.patch +++ /dev/null @@ -1,288 +0,0 @@ ---- a/src/api/wayfire/scene.hpp -+++ b/src/api/wayfire/scene.hpp -@@ -468,9 +468,11 @@ - TOP = 3, - UNMANAGED = 4, - OVERLAY = 5, -- LOCK = 6, -+ /** Desktop effects stop below this layer. Session locks remain above it. */ -+ SCREENSAVER = 6, -+ LOCK = 7, - // For compatibility with workspace-manager, to be removed -- DWIDGET = 7, -+ DWIDGET = 8, - /** Not a real layer, but a placeholder for the number of layers. */ - ALL_LAYERS, - }; ---- a/src/core/scene.cpp -+++ b/src/core/scene.cpp -@@ -175,6 +175,7 @@ - "top", - "unmanaged", - "overlay", -+ "screensaver", - "lock", - "dwidget" - }; ---- a/src/view/layer-shell/layer-shell.cpp -+++ b/src/view/layer-shell/layer-shell.cpp -@@ -480,7 +480,7 @@ - /* Keep the OLED screensaver above every normal overlay. */ - if (std::string{nonull(lsurface->namespace_t)} == "screensaver") - { -- return wf::scene::layer::LOCK; -+ return wf::scene::layer::SCREENSAVER; - } - - static const std::vector<std::string> desktop_widget_ids = { ---- a/src/api/wayfire/plugin.hpp -+++ b/src/api/wayfire/plugin.hpp -@@ -107,7 +107,7 @@ - /** - * The version is defined as macro as well, to allow conditional compilation. - */ --#define WAYFIRE_API_ABI_VERSION_MACRO 2026'07'26 -+#define WAYFIRE_API_ABI_VERSION_MACRO 2026'09'18 - - /** - * The version of Wayfire's API/ABI ---- a/plugins/ipc-rules/ipc-rules-common.hpp -+++ b/plugins/ipc-rules/ipc-rules-common.hpp -@@ -121,6 +121,9 @@ - - case wf::scene::layer::LOCK: - return "lock"; -+ -+ case wf::scene::layer::SCREENSAVER: -+ return "screensaver"; - - case wf::scene::layer::DWIDGET: - return "dew"; ---- a/src/output/workspace-stream.cpp -+++ b/src/output/workspace-stream.cpp -@@ -48,6 +48,15 @@ - - for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) - { -+ // A workspace contains desktop content only. In particular, cube/expo -+ // subpasses must never fold a screensaver or lock into the desktop. -+ if (output_node == self->output->node_for_layer(scene::layer::SCREENSAVER) || -+ output_node == self->output->node_for_layer(scene::layer::LOCK) || -+ output_node == self->output->node_for_layer(scene::layer::DWIDGET)) -+ { -+ continue; -+ } -+ - for (auto& ch : output_node->get_children()) - { - if (ch->is_enabled()) -@@ -150,7 +159,7 @@ - }; - - auto color = self->background.value_or(background_color_opt); -- data.pass->clear(data.damage, color); -+ data.pass->clear(data.damage, color, data.target); - } - - void presentation_feedback(wf::output_t *output) override ---- a/src/api/wayfire/render-manager.hpp -+++ b/src/api/wayfire/render-manager.hpp -@@ -72,6 +72,17 @@ - ~render_manager(); - - /** -+ * Render the composed scene strictly below SCREENSAVER to an offscreen target. -+ * Uses layout coordinates and the actual scene order (including custom nodes -+ * such as cube), without modifying live visibility, damage or frame callbacks. -+ * Excludes screensavers, locks, software cursors and final output postprocessing. -+ * Call from the compositor event loop, outside an active output render pass. -+ * Returns false for inhibited outputs, invalid targets or a failed submit. -+ * No work is performed between calls. The caller owns the target buffer. -+ */ -+ bool render_desktop(const render_target_t& target); -+ -+ /** - * Rendering an output is done on demand, that is, when the output is - * damaged. Some plugins however need to redraw the output as often as - * possible, for ex. when displaying some kind of animation. ---- a/src/api/wayfire/render.hpp -+++ b/src/api/wayfire/render.hpp -@@ -528,6 +528,8 @@ - * Do not clear the background areas. - */ - RPASS_CLEAR_BACKGROUND = (1 << 1), -+ /** Suppress presentation feedback, including feedback from nested subpasses. */ -+ RPASS_NO_PRESENTATION = (1 << 2), - }; - - /** -@@ -644,6 +646,10 @@ - */ - void clear(const wf::regionf_t& region, const wf::color_t& color); - -+ /** Clear a region expressed in an instruction's translated/scaled coordinates. */ -+ void clear(const wf::regionf_t& region, const wf::color_t& color, -+ const wf::render_target_t& adjusted_target); -+ - /** - * Add a texture rendering operation to the pass. - */ ---- a/src/render.cpp -+++ b/src/render.cpp -@@ -809,6 +809,18 @@ - - wf::regionf_t wf::render_pass_t::run_partial() - { -+ // Subpasses (cube faces, transformers, etc.) may supply their own reference -+ // output. An offscreen sample must not claim that any of these were presented. -+ static unsigned no_presentation_depth = 0; -+ struct feedback_guard_t -+ { -+ unsigned& depth; -+ bool suppress; -+ feedback_guard_t(unsigned& depth, bool suppress) : depth(depth), suppress(suppress) -+ { if (suppress) { ++depth; } } -+ ~feedback_guard_t() { if (suppress) { --depth; } } -+ } feedback_guard{no_presentation_depth, bool(params.flags & RPASS_NO_PRESENTATION)}; -+ - auto accumulated_damage = params.damage; - if (params.flags & RPASS_EMIT_SIGNALS) - { -@@ -844,7 +856,7 @@ - { - instr.pass = this; - instr.instance->render(instr); -- if (params.reference_output) -+ if (params.reference_output && !no_presentation_depth) - { - instr.instance->presentation_feedback(params.reference_output); - } -@@ -876,8 +888,14 @@ - - void wf::render_pass_t::clear(const wf::regionf_t& region, const wf::color_t& color) - { -- wlr_box box{0, 0, params.target.get_size().width, params.target.get_size().height}; -- auto damage = params.target.framebuffer_region_from_geometry_region(region); -+ clear(region, color, params.target); -+} -+ -+void wf::render_pass_t::clear(const wf::regionf_t& region, const wf::color_t& color, -+ const wf::render_target_t& adjusted_target) -+{ -+ wlr_box box{0, 0, adjusted_target.get_size().width, adjusted_target.get_size().height}; -+ auto damage = adjusted_target.framebuffer_region_from_geometry_region(region); - - wlr_render_rect_options opts; - opts.blend_mode = WLR_RENDER_BLEND_MODE_NONE; ---- a/src/output/render-manager.cpp -+++ b/src/output/render-manager.cpp -@@ -537,6 +537,11 @@ - class depth_buffer_manager_t - { - public: -+ bool is_required() const -+ { -+ return required_counter > 0; -+ } -+ - void ensure_depth_buffer(int fb, int width, int height) - { - /* If the backend doesn't have its own framebuffer, then the -@@ -1563,6 +1568,62 @@ - return pimpl->postprocessing->get_target_framebuffer(); - } - -+bool render_manager::render_desktop(const render_target_t& target) -+{ -+ auto output = pimpl->output; -+ if (output->is_inhibited() || pimpl->output_inhibit_counter || pimpl->current_pass || -+ !target.get_buffer() || target.geometry.width <= 0 || target.geometry.height <= 0) -+ { -+ return false; -+ } -+ -+ auto root = wf::get_core().scene(); -+ std::vector<scene::render_instance_uptr> instances; -+ bool below_screensaver = false; -+ for (const auto& child : root->get_children()) -+ { -+ if (child == root->layers[(size_t)scene::layer::SCREENSAVER]) -+ { -+ below_screensaver = true; -+ continue; -+ } -+ -+ if (below_screensaver && child->is_enabled()) -+ { -+ child->gen_render_instances(instances, [] (const wf::regionf_t&) {}, output); -+ } -+ } -+ -+ if (!below_screensaver) -+ { -+ return false; -+ } -+ -+ render_pass_params_t params; -+ params.instances = &instances; -+ params.target = target; -+ params.damage = wf::regionf_t{output->get_layout_geometry()} & target.geometry; -+ params.background_color = pimpl->background_color_opt; -+ params.renderer = output->handle->renderer; -+ params.flags = RPASS_CLEAR_BACKGROUND | RPASS_EMIT_SIGNALS | RPASS_NO_PRESENTATION; -+ // Cube and other 3D nodes require depth testing in offscreen passes too. -+ // Use an independent attachment; never resize or reuse the live output's. -+ depth_buffer_manager_t snapshot_depth; -+ if (wf::get_core().is_gles2() && pimpl->depth_buffer_manager->is_required()) -+ { -+ snapshot_depth.set_required(true); -+ const auto size = target.get_size(); -+ snapshot_depth.ensure_depth_buffer(gles::ensure_render_buffer_fb_id(target), -+ size.width, size.height); -+ } -+ -+ render_pass_t pass{params}; -+ pass.run_partial(); -+ bool submitted = pass.submit(); -+ snapshot_depth.frame_done(); -+ return submitted; -+} -+ - void render_manager::set_require_depth_buffer(bool require) - { - return pimpl->depth_buffer_manager->set_required(require); ---- a/plugins/cube/cube.cpp -+++ b/plugins/cube/cube.cpp -@@ -106,7 +106,9 @@ - auto cws = self->cube->output->wset()->get_current_workspace(); - output_background = std::make_shared<wf::workspace_stream_node_t>( - self->cube->output, cws); -- output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ // This pass owns the desktop beneath the transparent cube. -+ // Clear uncovered areas with the normal desktop background; -+ // leaving them transparent retains old pixels on live outputs. - output_background->include_workspace_views = false; - output_background->exclude_desktop_environment = self->cube->rotate_layer_shell; - auto push_background_damage = [=] (const wf::regionf_t& damage) ---- a/plugins/common/workspace-wall.cpp -+++ b/plugins/common/workspace-wall.cpp -@@ -190,7 +190,7 @@ - - void render(const wf::scene::render_instruction_t& data) override - { -- data.pass->clear(data.damage, self->wall->background_color); -+ data.pass->clear(data.damage, self->wall->background_color, data.target); - - auto damage = data.target.framebuffer_region_from_geometry_region(data.damage); - for (int i = 0; i < (int)self->workspaces.size(); i++) ---- a/plugins/single_plugins/switcher.cpp -+++ b/plugins/single_plugins/switcher.cpp -@@ -810,7 +810,7 @@ - - void render(const wf::scene::render_instruction_t& data) - { -- data.pass->clear(data.target.geometry, {0, 0, 0, 1}); -+ data.pass->clear(data.target.geometry, {0, 0, 0, 1}, data.target); - - auto local_target = data.target.translated(-wf::origin(render_node->get_bounding_box())); - for (auto view : get_background_views()) diff --git a/gui-wm/wayfire/files/wayfire-0.11.0-rotate-layer-shell.patch b/gui-wm/wayfire/files/wayfire-0.11.0-rotate-layer-shell.patch deleted file mode 100644 index 596fdc7f6d52..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.11.0-rotate-layer-shell.patch +++ /dev/null @@ -1,123 +0,0 @@ -diff -ruN wayfire-desktop-a3/metadata/cube.xml wayfire-desktop-b4/metadata/cube.xml ---- wayfire-desktop-a3/metadata/cube.xml 2026-09-18 18:32:36.218903728 -0500 -+++ wayfire-desktop-b4/metadata/cube.xml 2026-09-18 18:25:52.135123050 -0500 -@@ -131,6 +131,11 @@ - <hint>file</hint> - <default></default> - </option> -+ <option name="rotate_layer_shell" type="string"> -+ <_short>Rotate layer-shell namespace</_short> -+ <_long>Transforms a matching layer-shell namespace with the cube, for example waybar. Leave empty to keep layer-shell surfaces fixed.</_long> -+ <default></default> -+ </option> - <!-- Cubemap --> - <option name="cubemap_image" type="string"> - <_short>Cubemap image</_short> -diff -ruN wayfire-desktop-a3/plugins/cube/cube.cpp wayfire-desktop-b4/plugins/cube/cube.cpp ---- wayfire-desktop-a3/plugins/cube/cube.cpp 2026-09-18 18:32:36.218903728 -0500 -+++ wayfire-desktop-b4/plugins/cube/cube.cpp 2026-09-18 19:30:37.240755830 -0500 -@@ -108,6 +108,7 @@ - self->cube->output, cws); - output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; - output_background->include_workspace_views = false; -+ output_background->exclude_desktop_environment = self->cube->rotate_layer_shell; - auto push_background_damage = [=] (const wf::regionf_t& damage) - { - push_damage(self->get_bounding_box()); -@@ -258,6 +259,7 @@ - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; - wf::option_wrapper_t<double> face_dim{"cube/dim"}; - wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ wf::option_wrapper_t<std::string> rotate_layer_shell{"cube/rotate_layer_shell"}; - wf::option_wrapper_t<double> window_move_alpha{"vswitch/window_move_alpha"}; - bool input_is_grabbed = false; - wayfire_toplevel_view fixed_workspace_view = nullptr; -@@ -532,15 +534,15 @@ - { - auto root = wf::get_core().scene(); - auto children = root->get_children(); -- auto workspace_layer = -- root->layers[(int)wf::scene::layer::WORKSPACE]; -+ auto overlay_layer = -+ root->layers[(int)wf::scene::layer::OVERLAY]; - - std::vector<wf::scene::node_ptr> updated; - updated.reserve(children.size() + 1); - bool inserted = false; - for (auto& child : children) - { -- if (!inserted && (child == workspace_layer)) -+ if (!inserted && (child == overlay_layer)) - { - updated.push_back(render_node); - inserted = true; -@@ -1082,6 +1084,7 @@ - wf::scene::damage_node(render_node, render_node->get_bounding_box()); - if (animation.cube_animation.running()) - { -+ output->render->damage_whole(); - output->render->schedule_redraw(); - } else if (animation.in_exit) - { -diff -ruN wayfire-desktop-a3/src/api/wayfire/workspace-stream.hpp wayfire-desktop-b4/src/api/wayfire/workspace-stream.hpp ---- wayfire-desktop-a3/src/api/wayfire/workspace-stream.hpp 2026-09-18 18:32:36.219903732 -0500 -+++ wayfire-desktop-b4/src/api/wayfire/workspace-stream.hpp 2026-09-18 19:05:44.189920528 -0500 -@@ -25,6 +25,9 @@ - // should be included in the workspace stream. - bool include_desktop_environment = true; - -+ // Optional layer-shell namespace to omit from the fixed desktop pass. -+ std::string exclude_desktop_environment; -+ - // Whether regular workspace views should be included in the workspace - // stream. - bool include_workspace_views = true; -diff -ruN wayfire-desktop-a3/src/output/workspace-stream.cpp wayfire-desktop-b4/src/output/workspace-stream.cpp ---- wayfire-desktop-a3/src/output/workspace-stream.cpp 2026-09-18 18:32:36.219903732 -0500 -+++ wayfire-desktop-b4/src/output/workspace-stream.cpp 2026-09-18 19:30:52.482843865 -0500 -@@ -54,9 +54,28 @@ - { - auto view = node_to_view(ch); - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); -+ static wf::option_wrapper_t<std::string> rotate_layer_shell{ -+ "cube/rotate_layer_shell"}; -+ const std::string rotate_namespace = rotate_layer_shell; -+ const bool rotate_this_de = is_de && view && -+ !rotate_namespace.empty() && -+ (rotate_namespace == "*" || -+ view->get_app_id() == rotate_namespace); -+ const bool render_as_desktop_environment = is_de && !rotate_this_de; -+ const auto excluded_namespace = -+ self->exclude_desktop_environment; -+ const auto app_id = view ? view->get_app_id() : std::string{}; -+ const bool excluded_de = is_de && -+ !excluded_namespace.empty() && -+ (app_id == excluded_namespace || -+ app_id.rfind(excluded_namespace + "-", 0) == 0); -+ if (excluded_de) -+ { -+ continue; -+ } - size_t num_generated = this->instances.size(); - -- if (is_de && !self->include_desktop_environment) -+ if (is_de && !self->include_desktop_environment && !rotate_this_de) - { - continue; - } -@@ -69,13 +88,13 @@ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, -- is_de ? push_damage : translate_and_push_damage, self->output); -+ render_as_desktop_environment ? push_damage : translate_and_push_damage, self->output); - - // Mark whether the instances were generated from a desktop environment view - num_generated = this->instances.size() - num_generated; - for (size_t i = 0; i < num_generated; i++) - { -- is_desktop_environment.push_back(is_de); -+ is_desktop_environment.push_back(render_as_desktop_environment); - } - } - } diff --git a/gui-wm/wayfire/files/wayfire-0.11.0-screensaver-layer.patch b/gui-wm/wayfire/files/wayfire-0.11.0-screensaver-layer.patch deleted file mode 100644 index 4e7f99584777..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.11.0-screensaver-layer.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff -ruN wayfire-screensaver-a/src/view/layer-shell/layer-shell.cpp wayfire-screensaver-b/src/view/layer-shell/layer-shell.cpp ---- wayfire-screensaver-a/src/view/layer-shell/layer-shell.cpp 2026-07-27 03:26:17.000000000 -0500 -+++ wayfire-screensaver-b/src/view/layer-shell/layer-shell.cpp 2026-09-18 19:38:58.160150066 -0500 -@@ -477,6 +477,12 @@ - - wf::scene::layer wayfire_layer_shell_view::get_layer() - { -+ /* Keep the OLED screensaver above every normal overlay. */ -+ if (std::string{nonull(lsurface->namespace_t)} == "screensaver") -+ { -+ return wf::scene::layer::LOCK; -+ } -+ - static const std::vector<std::string> desktop_widget_ids = { - "keyboard", - "de-widget", diff --git a/gui-wm/wayfire/files/wayfire-0.11.0-transparent-cube.patch b/gui-wm/wayfire/files/wayfire-0.11.0-transparent-cube.patch deleted file mode 100644 index 8ac216855afc..000000000000 --- a/gui-wm/wayfire/files/wayfire-0.11.0-transparent-cube.patch +++ /dev/null @@ -1,1395 +0,0 @@ -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/metadata/cube.xml b/metadata/cube.xml ---- a/metadata/cube.xml 2026-07-27 03:26:17.000000000 -0500 -+++ b/metadata/cube.xml 2026-08-16 19:07:42.961288683 -0500 -@@ -49,6 +49,22 @@ - <_long>Specifies whether to use lighting.</_long> - <default>true</default> - </option> -+ <option name="dim" type="double"> -+ <_short>Cube dim</_short> -+ <_long>Darkens only the cube faces before they are blended over the output background.</_long> -+ <default>0.0</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> -+ <option name="resting_yaw" type="double"> -+ <_short>Resting yaw</_short> -+ <_long>Sets the initial cube turn used when interactive mouse rotation begins from rest.</_long> -+ <default>0.15</default> -+ <min>0.0</min> -+ <max>0.5</max> -+ <precision>0.01</precision> -+ </option> - <!-- Zoom --> - <option name="zoom" type="double"> - <_short>Zoom</_short> -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/metadata/vswitch.xml b/metadata/vswitch.xml ---- a/metadata/vswitch.xml 2026-07-27 03:26:17.000000000 -0500 -+++ b/metadata/vswitch.xml 2026-08-16 19:07:42.962288689 -0500 -@@ -54,6 +54,19 @@ - <_long>Switches to the last active workspace with the focused window with the specified activator.</_long> - <default></default> - </option> -+ <option name="with_win_modifier" type="activator"> -+ <_short>Hold window modifier</_short> -+ <_long>Highlights the focused window while the window workspace-switch modifier is held.</_long> -+ <default></default> -+ </option> -+ <option name="window_move_alpha" type="double"> -+ <_short>Window move alpha</_short> -+ <_long>Opacity used for the focused window while it is being held or moved between workspaces.</_long> -+ <default>0.65</default> -+ <min>0.0</min> -+ <max>1.0</max> -+ <precision>0.01</precision> -+ </option> - <option name="send_win_left" type="activator"> - <_short>Send window to the left</_short> - <_long>Send the focused window to the workspace on the left.</_long> -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/plugins/cube/cube-control-signal.hpp b/plugins/cube/cube-control-signal.hpp ---- a/plugins/cube/cube-control-signal.hpp 2026-07-27 03:26:17.000000000 -0500 -+++ b/plugins/cube/cube-control-signal.hpp 2026-08-16 19:07:42.962288689 -0500 -@@ -1,7 +1,9 @@ - #ifndef CUBE_CONTROL_SIGNAL - #define CUBE_CONTROL_SIGNAL - -+#include <wayfire/geometry.hpp> - #include <wayfire/signal-definitions.hpp> -+#include <wayfire/toplevel-view.hpp> - - /* A private signal, currently shared by idle & cube - * -@@ -18,4 +20,18 @@ - bool carried_out; // false if cube is disabled - }; - -+struct cube_workspace_animation_signal -+{ -+ wf::point_t delta; -+ wf::point_t anchor_workspace; -+ wayfire_toplevel_view fixed_view = nullptr; -+ bool carried_out = false; -+}; -+ -+struct cube_render_pause_signal -+{ -+ bool pause = false; -+ bool carried_out = false; -+}; -+ - #endif /* end of include guard: CUBE_CONTROL_SIGNAL */ -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/plugins/cube/cube.cpp b/plugins/cube/cube.cpp ---- a/plugins/cube/cube.cpp 2026-07-27 03:26:17.000000000 -0500 -+++ b/plugins/cube/cube.cpp 2026-08-16 19:07:52.511342641 -0500 -@@ -1,15 +1,22 @@ - #include <wayfire/per-output-plugin.hpp> - #include <memory> -+#include <optional> - #include <wayfire/plugin.hpp> - #include <wayfire/opengl.hpp> - #include <wayfire/output.hpp> - #include <wayfire/core.hpp> -+#include <wayfire/window-manager.hpp> -+#include <wayfire/seat.hpp> - #include <wayfire/workspace-stream.hpp> - #include <wayfire/render-manager.hpp> - #include <wayfire/workspace-set.hpp> - #include <wayfire/scene-operations.hpp> -+#include <wayfire/unstable/translation-node.hpp> -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> - #include <wayfire/plugins/common/input-grab.hpp> - #include "wayfire/plugins/ipc/ipc-activator.hpp" -+#include <wlr/types/wlr_keyboard.h> - - #include <glm/gtc/matrix_transform.hpp> - #include <wayfire/img.hpp> -@@ -39,6 +46,38 @@ - - class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t - { -+ class fixed_view_overlay_node_t : public wf::scene::node_t -+ { -+ std::weak_ptr<wf::toplevel_view_interface_t> view; -+ -+ public: -+ fixed_view_overlay_node_t(wayfire_toplevel_view view) : node_t(false) -+ { -+ this->view = view->weak_from_this(); -+ } -+ -+ void gen_render_instances( -+ std::vector<wf::scene::render_instance_uptr>& instances, -+ wf::scene::damage_callback push_damage, wf::output_t *shown_on) override -+ { -+ if (auto view = this->view.lock()) -+ { -+ view->get_transformed_node()->gen_render_instances( -+ instances, push_damage, shown_on); -+ } -+ } -+ -+ wf::geometry_t get_bounding_box() override -+ { -+ if (auto view = this->view.lock()) -+ { -+ return view->get_transformed_node()->get_bounding_box(); -+ } -+ -+ return {0, 0, 0, 0}; -+ } -+ }; -+ - class cube_render_node_t : public wf::scene::node_t - { - class cube_render_instance_t : public wf::scene::render_instance_t -@@ -46,6 +85,8 @@ - std::shared_ptr<cube_render_node_t> self; - wf::scene::damage_callback push_damage; - -+ std::shared_ptr<wf::workspace_stream_node_t> output_background; -+ std::vector<wf::scene::render_instance_uptr> output_background_instances; - std::vector<std::vector<wf::scene::render_instance_uptr>> ws_instances; - std::vector<wf::regionf_t> ws_damage; - std::vector<wf::auxilliary_buffer_t> framebuffers; -@@ -61,6 +102,19 @@ - { - this->self = std::dynamic_pointer_cast<cube_render_node_t>(self->shared_from_this()); - this->push_damage = push_damage; -+ -+ auto cws = self->cube->output->wset()->get_current_workspace(); -+ output_background = std::make_shared<wf::workspace_stream_node_t>( -+ self->cube->output, cws); -+ output_background->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ output_background->include_workspace_views = false; -+ auto push_background_damage = [=] (const wf::regionf_t& damage) -+ { -+ push_damage(self->get_bounding_box()); -+ }; -+ output_background->gen_render_instances(output_background_instances, -+ push_background_damage, self->cube->output); -+ - self->connect(&on_cube_damage); - - ws_damage.resize(self->workspaces.size()); -@@ -94,18 +148,25 @@ - .damage = damage & self->get_bounding_box(), - }); - -+ wf::regionf_t background_damage = -+ output_background->get_bounding_box(); -+ auto background_target = -+ target.translated(-wf::origin(self->get_bounding_box())); -+ for (auto& instance : output_background_instances) -+ { -+ instance->schedule_instructions( -+ instructions, background_target, background_damage); -+ } -+ - auto bbox = self->get_bounding_box(); - - damage ^= bbox; - -- const bool is_hdr = self->cube->output && self->cube->output->is_hdr(); -- - for (int i = 0; i < (int)ws_instances.size(); i++) - { - const float scale = self->cube->output->handle->scale; - auto bbox = self->workspaces[i]->get_bounding_box(); -- framebuffers[i].allocate(wf::dimensions(bbox), scale, -- wf::buffer_allocation_hints_t{.hdr_linear = is_hdr}); -+ framebuffers[i].allocate(wf::dimensions(bbox), scale); - - wf::render_target_t target{framebuffers[i]}; - target.geometry = self->workspaces[i]->get_bounding_box(); -@@ -113,9 +174,10 @@ - - wf::render_pass_params_t params; - params.instances = &ws_instances[i]; -- params.damage = ws_damage[i]; -+ params.damage = self->workspaces[i]->get_bounding_box(); - params.reference_output = self->cube->output; - params.target = target; -+ params.background_color = wf::color_t{0.0, 0.0, 0.0, 0.0}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_EMIT_SIGNALS; - - wf::render_pass_t::run(params); -@@ -149,7 +211,10 @@ - auto y = cube->output->wset()->get_current_workspace().y; - for (int i = 0; i < w; i++) - { -- auto node = std::make_shared<wf::workspace_stream_node_t>(cube->output, wf::point_t{i, y}); -+ auto node = std::make_shared<wf::workspace_stream_node_t>( -+ cube->output, wf::point_t{i, y}); -+ node->background = wf::color_t{0.0, 0.0, 0.0, 0.0}; -+ node->include_desktop_environment = false; - workspaces.push_back(node); - } - } -@@ -179,10 +244,24 @@ - - std::unique_ptr<wf::input_grab_t> input_grab; - std::shared_ptr<cube_render_node_t> render_node; -+ std::shared_ptr<wf::scene::translation_node_t> fixed_overlay_node; -+ wayfire_toplevel_view fixed_overlay_view = nullptr; -+ wayfire_toplevel_view post_move_dim_view = nullptr; -+ std::optional<wf::point_t> workspace_animation_anchor; -+ const std::string held_dim_transformer_name = "workspace-move-window-dim"; -+ bool fixed_overlay_added_transformer = false; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - wf::option_wrapper_t<double> XVelocity{"cube/speed_spin_horiz"}, - YVelocity{"cube/speed_spin_vert"}, ZVelocity{"cube/speed_zoom"}; - wf::option_wrapper_t<double> zoom_opt{"cube/zoom"}; -+ wf::option_wrapper_t<double> face_dim{"cube/dim"}; -+ wf::option_wrapper_t<double> resting_yaw{"cube/resting_yaw"}; -+ wf::option_wrapper_t<double> window_move_alpha{"vswitch/window_move_alpha"}; -+ bool input_is_grabbed = false; -+ wayfire_toplevel_view fixed_workspace_view = nullptr; -+ bool cube_render_paused = false; - - /* the Z camera distance so that (-1, 1) is mapped to the whole screen - * for the given FOV */ -@@ -234,7 +313,7 @@ - - wf::plugin_activation_data_t grab_interface{ - .name = "cube", -- .capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR, -+ .capabilities = 0, - .cancel = [=] () { deactivate(); }, - }; - -@@ -255,6 +334,13 @@ - reload_background(); - - output->connect(&on_cube_control); -+ output->connect(&on_cube_workspace_animation); -+ output->connect(&on_cube_render_pause); -+ output->connect(&on_fixed_view_disappeared); -+ output->connect(&on_view_geometry_changed); -+ wf::get_core().connect(&on_fixed_view_set_output); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - wf::gles::run_in_context([&] - { - load_program(); -@@ -358,6 +444,266 @@ - output->render->schedule_redraw(); - } - -+ void animate_workspace_delta(wf::point_t delta, -+ wf::point_t anchor_workspace, -+ wayfire_toplevel_view fixed_view) -+ { -+ if (!activate()) -+ { -+ return; -+ } -+ -+ if (fixed_view && (fixed_view->get_output() != output)) -+ { -+ fixed_view = nullptr; -+ } -+ -+ workspace_animation_anchor = anchor_workspace; -+ fixed_workspace_view = fixed_view; -+ show_fixed_workspace_overlay(fixed_view); -+ rebuild_cube_render_node(); -+ -+ animation.in_exit = false; -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ if (delta.x != 0) -+ { -+ animation.cube_animation.rotation.set( -+ delta.x * animation.side_angle, -+ delta.x * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); -+ } else -+ { -+ animation.cube_animation.rotation.set(0, 0); -+ } -+ -+ animation.cube_animation.start(); -+ update_view_matrix(); -+ output->render->schedule_redraw(); -+ } -+ -+ wf::signal::connection_t<cube_workspace_animation_signal> -+ on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) -+ { -+ animate_workspace_delta(ev->delta, ev->anchor_workspace, -+ ev->fixed_view); -+ ev->carried_out = true; -+ }; -+ -+ wf::signal::connection_t<cube_render_pause_signal> -+ on_cube_render_pause = [=] (cube_render_pause_signal *ev) -+ { -+ pause_cube_render(ev->pause); -+ ev->carried_out = true; -+ }; -+ -+ void grab_cube_input() -+ { -+ if (input_is_grabbed) -+ { -+ return; -+ } -+ -+ wf::get_core().connect(&on_motion_event); -+ wf::get_core().hide_cursor(); -+ input_grab->grab_input(wf::scene::layer::OVERLAY); -+ input_is_grabbed = true; -+ } -+ -+ void release_cube_input() -+ { -+ if (!input_is_grabbed) -+ { -+ return; -+ } -+ -+ input_grab->ungrab_input(); -+ wf::get_core().unhide_cursor(); -+ on_motion_event.disconnect(); -+ input_is_grabbed = false; -+ } -+ -+ void add_cube_render_node() -+ { -+ auto root = wf::get_core().scene(); -+ auto children = root->get_children(); -+ auto workspace_layer = -+ root->layers[(int)wf::scene::layer::WORKSPACE]; -+ -+ std::vector<wf::scene::node_ptr> updated; -+ updated.reserve(children.size() + 1); -+ bool inserted = false; -+ for (auto& child : children) -+ { -+ if (!inserted && (child == workspace_layer)) -+ { -+ updated.push_back(render_node); -+ inserted = true; -+ } -+ -+ updated.push_back(child); -+ } -+ -+ if (!inserted) -+ { -+ updated.insert(updated.begin(), render_node); -+ } -+ -+ root->set_children_list(updated); -+ wf::scene::update(root, wf::scene::update_flag::CHILDREN_LIST); -+ } -+ -+ void rebuild_cube_render_node() -+ { -+ if (!render_node) -+ { -+ return; -+ } -+ -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ void pause_cube_render(bool pause) -+ { -+ if (pause) -+ { -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = nullptr; -+ cube_render_paused = true; -+ output->render->damage_whole(); -+ } -+ -+ return; -+ } -+ -+ if (cube_render_paused && !render_node) -+ { -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ cube_render_paused = false; -+ output->render->damage_whole(); -+ } -+ } -+ -+ void remove_held_dim_transformers(wayfire_toplevel_view view) -+ { -+ if (!view) -+ { -+ return; -+ } -+ -+ auto node = view->get_transformed_node(); -+ while (node->get_transformer(held_dim_transformer_name)) -+ { -+ node->rem_transformer(held_dim_transformer_name); -+ } -+ } -+ -+ bool has_held_dim_transformer(wayfire_toplevel_view view) -+ { -+ if (!view) -+ { -+ return false; -+ } -+ -+ return view->get_transformed_node()->get_transformer( -+ held_dim_transformer_name) != nullptr; -+ } -+ -+ void clear_fixed_workspace_overlay(bool keep_post_move_dim = true) -+ { -+ if (!fixed_overlay_node) -+ { -+ return; -+ } -+ -+ if (fixed_overlay_view) -+ { -+ if (keep_post_move_dim && -+ ((wf::get_core().seat->get_keyboard_modifiers() & -+ held_window_modifiers) == held_window_modifiers)) -+ { -+ post_move_dim_view = fixed_overlay_view; -+ fixed_overlay_view = nullptr; -+ return; -+ } else -+ { -+ if (fixed_overlay_added_transformer) -+ { -+ remove_held_dim_transformers(fixed_overlay_view); -+ } -+ -+ post_move_dim_view = nullptr; -+ } -+ -+ wf::scene::set_node_enabled( -+ fixed_overlay_view->get_transformed_node(), true); -+ fixed_overlay_view = nullptr; -+ fixed_overlay_added_transformer = false; -+ } -+ -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ void clear_post_move_window_dim() -+ { -+ if (!post_move_dim_view) -+ { -+ return; -+ } -+ -+ remove_held_dim_transformers(post_move_dim_view); -+ wf::scene::set_node_enabled( -+ post_move_dim_view->get_transformed_node(), true); -+ if (fixed_overlay_node) -+ { -+ wf::scene::remove_child(fixed_overlay_node); -+ fixed_overlay_node = nullptr; -+ } -+ -+ post_move_dim_view = nullptr; -+ fixed_overlay_added_transformer = false; -+ } -+ -+ void show_fixed_workspace_overlay(wayfire_toplevel_view view) -+ { -+ clear_post_move_window_dim(); -+ clear_fixed_workspace_overlay(false); -+ if (!view) -+ { -+ return; -+ } -+ -+ auto overlay = std::make_shared<fixed_view_overlay_node_t>(view); -+ fixed_overlay_added_transformer = false; -+ if (!has_held_dim_transformer(view)) -+ { -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = wf::clamp((double)window_move_alpha, 0.0, 1.0); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ fixed_overlay_added_transformer = true; -+ } -+ -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ fixed_overlay_view = view; -+ -+ fixed_overlay_node = std::make_shared<wf::scene::translation_node_t>(); -+ fixed_overlay_node->set_children_list({overlay}); -+ fixed_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), fixed_overlay_node); -+ } -+ - /* Tries to initialize renderer, activate plugin, etc. */ - bool activate() - { -@@ -371,16 +717,11 @@ - return false; - } - -- wf::get_core().connect(&on_motion_event); -- - render_node = std::make_shared<cube_render_node_t>(this); -- wf::scene::add_front(wf::get_core().scene(), render_node); -+ add_cube_render_node(); - output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); - output->render->set_require_depth_buffer(true); - -- wf::get_core().hide_cursor(); -- input_grab->grab_input(wf::scene::layer::OVERLAY); -- - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); -@@ -418,19 +759,21 @@ - output->render->rem_effect(&pre_hook); - output->render->set_require_depth_buffer(false); - -- input_grab->ungrab_input(); -- output->deactivate_plugin(&grab_interface); -- wf::get_core().unhide_cursor(); -- on_motion_event.disconnect(); -- - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - -- auto cws = output->wset()->get_current_workspace(); -+ auto cws = workspace_animation_anchor.value_or( -+ output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); - -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ release_cube_input(); -+ output->deactivate_plugin(&grab_interface); -+ - /* We are finished with rotation, make sure the next time cube is used - * it is properly reset */ - animation.cube_animation.rotation.set(0, 0); -@@ -460,13 +803,36 @@ - - /* After the rotation is done, we want to exit cube and focus the target - * workspace */ -- animation.in_exit = true; -+ animation.in_exit = false; - - /* Set up rotation target to the next workspace in the given direction, - * and reset other attribs */ -- reset_attribs(); -- animation.cube_animation.rotation.restart_with_end( -- animation.cube_animation.rotation.end - dir * animation.side_angle); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ auto cws = output->wset()->get_current_workspace(); -+ int size = get_num_faces(); -+ int nvx = (cws.x + dir + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); -+ -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ animation.cube_animation.rotation.set( -+ dir * animation.side_angle, dir * animation.side_angle); -+ animation.cube_animation.rotation.restart_with_end(0); - - animation.cube_animation.start(); - update_view_matrix(); -@@ -493,6 +859,14 @@ - float current_offset_y = animation.cube_animation.offset_y; - float current_zoom = animation.cube_animation.zoom; - -+ grab_cube_input(); -+ -+ if ((current_rotation > -1e-6) && (current_rotation < 1e-6)) -+ { -+ current_rotation = animation.side_angle * -+ wf::clamp((double)resting_yaw, 0.0, 0.5); -+ } -+ - animation.cube_animation.rotation.set(current_rotation, current_rotation); - animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); - animation.cube_animation.offset_z.restart_with_end( -@@ -513,19 +887,47 @@ - /* Mouse grab was released */ - void input_ungrabbed() - { -- animation.in_exit = true; -+ release_cube_input(); -+ animation.in_exit = false; - -- /* Rotate cube so that selected workspace aligns with the output */ - float current_rotation = animation.cube_animation.rotation; -- int dvx = calculate_viewport_dx_from_rotation(); -- animation.cube_animation.rotation.set(current_rotation, -- -dvx * animation.side_angle); -- /* And reset other attributes, again to align the workspace with the output -- * */ -- reset_attribs(); -+ int size = get_num_faces(); -+ int dvx = calculate_viewport_dx_from_rotation(); -+ auto cws = output->wset()->get_current_workspace(); -+ int nvx = (cws.x + (dvx % size) + size) % size; -+ output->wset()->set_workspace({nvx, cws.y}); -+ wf::get_core().seat->focus_output(output); - -- animation.cube_animation.start(); -+ auto views = output->wset()->get_views( -+ wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED | -+ wf::WSET_SORT_STACKING, wf::point_t{nvx, cws.y}); -+ for (auto it = views.rbegin(); it != views.rend(); ++it) -+ { -+ if ((*it)->is_focusable() && (*it)->get_keyboard_focus_surface()) -+ { -+ wf::get_core().default_wm->focus_raise_view(*it, false); -+ break; -+ } -+ } -+ -+ if (render_node) -+ { -+ wf::scene::remove_child(render_node); -+ render_node = std::make_shared<cube_render_node_t>(this); -+ add_cube_render_node(); -+ } -+ -+ current_rotation = 0; - -+ animation.cube_animation.rotation.set(current_rotation, current_rotation); -+ animation.cube_animation.offset_y.set(0, 0); -+ animation.cube_animation.offset_z.set( -+ identity_z_offset + Z_OFFSET_NEAR, -+ identity_z_offset + Z_OFFSET_NEAR); -+ animation.cube_animation.zoom.set(1.0, 1.0); -+ animation.cube_animation.ease_deformation.set(0, 0); -+ -+ animation.cube_animation.start(); - update_view_matrix(); - output->render->schedule_redraw(); - } -@@ -596,7 +998,6 @@ - { - int index = (cws.x + i) % get_num_faces(); - GL_CALL(glBindTexture(GL_TEXTURE_2D, wf::gles_texture_t::from_aux(buffers[index]).tex_id)); -- - auto model = calculate_model_matrix(i); - program.uniformMatrix4f("model", model); - -@@ -623,7 +1024,7 @@ - } - - GL_CALL(glClear(GL_DEPTH_BUFFER_BIT)); -- background->render_frame(data.target, animation); -+ /* The fixed output background is scheduled behind the cube. */ - - auto vp = calculate_vp_matrix(data.target); - -@@ -648,6 +1049,8 @@ - program.attrib_pointer("position", 2, 0, vertexData); - program.attrib_pointer("uvPosition", 2, 0, coordData); - program.uniformMatrix4f("VP", vp); -+ program.uniform1f("cube_brightness", -+ 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); - if (tessellation_support) - { - program.uniform1i("deform", use_deform); -@@ -660,10 +1063,13 @@ - * By using two stages, we ensure that we first render the cube sides - * that are on the back, and then we render those at the front, so we - * don't have to use depth testing and we also can support alpha cube. */ -+ GL_CALL(glEnable(GL_BLEND)); -+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GL_CALL(glEnable(GL_CULL_FACE)); - render_cube(GL_CCW, buffers); - render_cube(GL_CW, buffers); - GL_CALL(glDisable(GL_CULL_FACE)); -+ GL_CALL(glDisable(GL_BLEND)); - - GL_CALL(glDisable(GL_DEPTH_TEST)); - program.deactivate(); -@@ -680,6 +1086,16 @@ - } else if (animation.in_exit) - { - deactivate(); -+ } else { -+ if (fixed_workspace_view) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_fixed_workspace_overlay(); -+ rebuild_cube_render_node(); -+ } -+ -+ output->render->schedule_redraw(); - } - }; - -@@ -754,8 +1170,83 @@ - output->render->schedule_redraw(); - } - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) != held_window_modifiers) -+ { -+ clear_post_move_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> -+ on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!post_move_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_window_modifiers) -+ { -+ clear_post_move_window_dim(); -+ } -+ }; -+ -+ void clear_if_fixed_view(wayfire_view view) -+ { -+ if ((view == fixed_overlay_view) || (view == post_move_dim_view) || -+ (view == fixed_workspace_view)) -+ { -+ fixed_workspace_view = nullptr; -+ workspace_animation_anchor.reset(); -+ clear_post_move_window_dim(); -+ clear_fixed_workspace_overlay(false); -+ rebuild_cube_render_node(); -+ output->render->schedule_redraw(); -+ } -+ } -+ -+ wf::signal::connection_t<wf::view_disappeared_signal> -+ on_fixed_view_disappeared = [=] (wf::view_disappeared_signal *ev) -+ { -+ clear_if_fixed_view(ev->view); -+ }; -+ -+ wf::signal::connection_t<wf::view_set_output_signal> -+ on_fixed_view_set_output = [=] (wf::view_set_output_signal *ev) -+ { -+ if ((ev->view == fixed_overlay_view) || (ev->view == post_move_dim_view) || -+ (ev->view == fixed_workspace_view)) -+ { -+ if (ev->view->get_output() != output) -+ { -+ clear_if_fixed_view(ev->view); -+ } -+ } -+ }; -+ -+ wf::signal::connection_t<wf::view_geometry_changed_signal> -+ on_view_geometry_changed = [=] (wf::view_geometry_changed_signal *ev) -+ { -+ (void)ev; -+ if (!fixed_workspace_view || !render_node) -+ { -+ return; -+ } -+ -+ rebuild_cube_render_node(); -+ output->render->schedule_redraw(); -+ }; -+ - void fini() override - { -+ clear_post_move_window_dim(); - if (output->is_plugin_active(grab_interface.name)) - { - deactivate(); -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/plugins/cube/cubemap.cpp b/plugins/cube/cubemap.cpp ---- a/plugins/cube/cubemap.cpp 2026-07-27 03:26:17.000000000 -0500 -+++ b/plugins/cube/cubemap.cpp 2026-08-16 19:07:42.962288689 -0500 -@@ -84,6 +84,12 @@ - void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_cubemap::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - reload_texture(); - - if (tex == (uint32_t)-1) -@@ -136,13 +142,17 @@ - glEnableVertexAttribArray(vertex); - glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation), -- glm::vec3(0, 1, 0)); -- -- glm::vec3 look_at{0., -- (double)-attribs.cube_animation.offset_y, -- (double)attribs.cube_animation.offset_z}; -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation), -+ glm::vec3(0, 1, 0)) : -+ glm::mat4(1.0); -+ -+ glm::vec3 look_at = follow_cube ? -+ glm::vec3{0., -+ (double)-attribs.cube_animation.offset_y, -+ (double)attribs.cube_animation.offset_z} : -+ glm::vec3{0., 0., 1.}; - - auto view = glm::lookAt(glm::vec3(0., 0., 0.), look_at, glm::vec3(0., 1., 0.)); - auto vp = wf::gles::output_transform(fb) * attribs.projection * view; -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/plugins/cube/cubemap.hpp b/plugins/cube/cubemap.hpp ---- a/plugins/cube/cubemap.hpp 2026-07-27 03:26:17.000000000 -0500 -+++ b/plugins/cube/cubemap.hpp 2026-08-16 19:07:42.962288689 -0500 -@@ -13,6 +13,8 @@ - ~wf_cube_background_cubemap(); - - private: -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void reload_texture(); - void create_program(); - -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/plugins/cube/shaders-3-2.tpp b/plugins/cube/shaders-3-2.tpp ---- a/plugins/cube/shaders-3-2.tpp 2026-07-27 03:26:17.000000000 -0500 -+++ b/plugins/cube/shaders-3-2.tpp 2026-08-16 19:07:42.962288689 -0500 -@@ -131,7 +131,7 @@ - float d = distance(center, lightSource); - float ambient_coeff = pow(clamp(2.0 / d, 0.0, 1.0), 10.0); - -- float value = clamp(pow(abs(dot(N, lightNormal)), 1.5), 0.0, 1.0); -+ float value = clamp(pow(max(dot(N, lightNormal), 0.0), 1.5), 0.0, 1.0); - - float df = AL * ambient_coeff + DL * value; - colorFactor = vec3(df, df, df); -@@ -160,7 +160,9 @@ - layout(location = 0) out highp vec4 outColor; - - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- outColor = vec4(texture(smp, guv).xyz * colorFactor, 1.0); -+ highp vec4 tex = texture(smp, guv); -+ outColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/plugins/cube/shaders.tpp b/plugins/cube/shaders.tpp ---- a/plugins/cube/shaders.tpp 2026-07-27 03:26:17.000000000 -0500 -+++ b/plugins/cube/shaders.tpp 2026-08-16 19:07:42.962288689 -0500 -@@ -19,7 +19,9 @@ - R"(#version 100 - varying highp vec2 uvpos; - uniform sampler2D smp; -+uniform highp float cube_brightness; - - void main() { -- gl_FragColor = vec4(texture2D(smp, uvpos).xyz, 1); -+ highp vec4 tex = texture2D(smp, uvpos); -+ gl_FragColor = vec4(tex.xyz * cube_brightness, tex.a); - })"; -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/plugins/cube/skydome.cpp b/plugins/cube/skydome.cpp ---- a/plugins/cube/skydome.cpp 2026-07-27 03:26:17.000000000 -0500 -+++ b/plugins/cube/skydome.cpp 2026-08-16 19:07:42.962288689 -0500 -@@ -129,6 +129,12 @@ - void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, - wf_cube_animation_attribs& attribs) - { -+ render_frame(fb, attribs, true); -+} -+ -+void wf_cube_background_skydome::render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube) -+{ - fill_vertices(); - reload_texture(); - -@@ -143,13 +149,19 @@ - wf::gles::bind_render_buffer(fb); - program.use(wf::TEXTURE_TYPE_RGBA); - -- auto rotation = glm::rotate(glm::mat4(1.0), -- (float)(attribs.cube_animation.offset_y * 0.5), -- glm::vec3(1., 0., 0.)); -- -- auto view = glm::lookAt(glm::vec3(0., 0., 0.), -- glm::vec3(0., 0., -attribs.cube_animation.offset_z), -- glm::vec3(0., 1., 0.)); -+ auto rotation = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ (float)(attribs.cube_animation.offset_y * 0.5), -+ glm::vec3(1., 0., 0.)) : -+ glm::mat4(1.0); -+ -+ auto view = follow_cube ? -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -attribs.cube_animation.offset_z), -+ glm::vec3(0., 1., 0.)) : -+ glm::lookAt(glm::vec3(0., 0., 0.), -+ glm::vec3(0., 0., -1.), -+ glm::vec3(0., 1., 0.)); - - auto vp = wf::gles::output_transform(fb) * attribs.projection * view * rotation; - program.uniformMatrix4f("VP", vp); -@@ -158,9 +170,13 @@ - program.attrib_pointer("uvPosition", 2, 0, coords.data()); - - auto cws = output->wset()->get_current_workspace(); -- auto model = glm::rotate(glm::mat4(1.0), -- float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -- glm::vec3(0, 1, 0)); -+ auto model = follow_cube ? -+ glm::rotate(glm::mat4(1.0), -+ float(attribs.cube_animation.rotation) - cws.x * attribs.side_angle, -+ glm::vec3(0, 1, 0)) : -+ glm::rotate(glm::mat4(1.0), -+ float(-cws.x * attribs.side_angle), -+ glm::vec3(0, 1, 0)); - - program.uniformMatrix4f("model", model); - -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/plugins/cube/skydome.hpp b/plugins/cube/skydome.hpp ---- a/plugins/cube/skydome.hpp 2026-07-27 03:26:17.000000000 -0500 -+++ b/plugins/cube/skydome.hpp 2026-08-16 19:07:42.962288689 -0500 -@@ -17,6 +17,8 @@ - private: - wf::output_t *output; - -+ void render_frame(const wf::render_target_t& fb, -+ wf_cube_animation_attribs& attribs, bool follow_cube); - void load_program(); - void fill_vertices(); - void reload_texture(); -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/plugins/vswitch/vswitch.cpp b/plugins/vswitch/vswitch.cpp ---- a/plugins/vswitch/vswitch.cpp 2026-07-27 03:26:17.000000000 -0500 -+++ b/plugins/vswitch/vswitch.cpp 2026-08-16 19:07:42.962288689 -0500 -@@ -10,6 +10,10 @@ - #include "wayfire/unstable/translation-node.hpp" - #include "wayfire/scene-operations.hpp" - #include "wayfire/render-manager.hpp" -+#include <wayfire/view-transform.hpp> -+#include <wayfire/nonstd/wlroots.hpp> -+#include <wlr/types/wlr_keyboard.h> -+#include "../cube/cube-control-signal.hpp" - - namespace wf - { -@@ -178,7 +182,7 @@ - /** - * Set the overlay view. It will be hidden from the normal workspace layers - * and shown on top of the workspace wall. The overlay view's position is -- * not animated together with the workspace transition, but its alpha is. -+ * not animated together with the workspace transition. - * - * Note: if the view disappears, the caller is responsible for resetting the - * overlay view. -@@ -280,28 +284,12 @@ - return; - } - -- double progress = animation.progress(); -- - auto tmanager = overlay_view->get_transformed_node(); - auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>( - vswitch_view_transformer_name); - -- static constexpr double smoothing_in = 0.4; -- static constexpr double smoothing_out = 0.2; -- static constexpr double smoothing_amount = 0.5; -- - tmanager->begin_transform_update(); -- if (progress <= smoothing_in) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress; -- } else if (progress >= 1.0 - smoothing_out) -- { -- tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress); -- } else -- { -- tr->alpha = smoothing_amount; -- } -- -+ tr->alpha = 1.0; - tmanager->end_transform_update(); - } - -@@ -380,6 +368,13 @@ - - std::unique_ptr<vswitch_basic_plugin> algorithm; - std::unique_ptr<wf::vswitch::control_bindings_t> bindings; -+ wayfire_toplevel_view held_dim_view = nullptr; -+ std::shared_ptr<wf::scene::translation_node_t> held_dim_overlay_node; -+ uint32_t held_dim_modifiers = 0; -+ const std::string held_dim_transformer_name = "workspace-move-window-dim"; -+ wf::option_wrapper_t<double> window_move_alpha{"vswitch/window_move_alpha"}; -+ static constexpr uint32_t held_window_modifiers = -+ WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO; - - // Capabilities which are always required for vswitch, for now wall needs - // a custom renderer. -@@ -387,7 +382,11 @@ - - wf::plugin_activation_data_t grab_interface = { - .name = "vswitch", -- .cancel = [=] () { algorithm->stop_switch(false); }, -+ .cancel = [=] () -+ { -+ clear_held_window_dim(); -+ algorithm->stop_switch(false); -+ }, - }; - - public: -@@ -395,9 +394,15 @@ - { - output->connect(&on_set_workspace_request); - output->connect(&on_grabbed_view_disappear); -+ wf::get_core().connect(&on_key_release); -+ wf::get_core().connect(&on_modifier_change); - - algorithm = std::make_unique<vswitch_basic_plugin>(output, -- [=] () { output->deactivate_plugin(&grab_interface); }); -+ [=] () -+ { -+ clear_held_window_dim(); -+ output->deactivate_plugin(&grab_interface); -+ }); - - bindings = std::make_unique<wf::vswitch::control_bindings_t>(output); - bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) -@@ -413,6 +418,11 @@ - { - if (delta == wf::point_t{0, 0}) - { -+ if (view) -+ { -+ show_held_window_dim(view); -+ } -+ - // Consume input event - return true; - } -@@ -446,6 +456,91 @@ - return is_active() || output->can_activate_plugin(&grab_interface); - } - -+ void remove_held_dim_transformers(wayfire_toplevel_view view) -+ { -+ if (!view) -+ { -+ return; -+ } -+ -+ auto node = view->get_transformed_node(); -+ while (node->get_transformer(held_dim_transformer_name)) -+ { -+ node->rem_transformer(held_dim_transformer_name); -+ } -+ } -+ -+ void clear_held_window_dim() -+ { -+ if (held_dim_view) -+ { -+ remove_held_dim_transformers(held_dim_view); -+ wf::scene::set_node_enabled(held_dim_view->get_transformed_node(), true); -+ } -+ -+ if (held_dim_overlay_node) -+ { -+ wf::scene::remove_child(held_dim_overlay_node); -+ held_dim_overlay_node = nullptr; -+ } -+ -+ held_dim_view = nullptr; -+ held_dim_modifiers = 0; -+ } -+ -+ wayfire_toplevel_view get_held_window_target_view() -+ { -+ auto view = find_topmost_parent( -+ toplevel_cast(wf::get_core().seat->get_active_view())); -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL)) -+ { -+ return nullptr; -+ } -+ -+ return view; -+ } -+ -+ void show_held_window_dim(wayfire_toplevel_view view) -+ { -+ if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped()) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (view->get_output() != output) -+ { -+ clear_held_window_dim(); -+ return; -+ } -+ -+ if (held_dim_view == view) -+ { -+ return; -+ } -+ -+ clear_held_window_dim(); -+ -+ auto transformer = std::make_shared<wf::scene::view_2d_transformer_t>(view); -+ transformer->alpha = wf::clamp((double)window_move_alpha, 0.0, 1.0); -+ remove_held_dim_transformers(view); -+ view->get_transformed_node()->add_transformer(transformer, -+ wf::TRANSFORMER_2D, held_dim_transformer_name); -+ wf::scene::set_node_enabled(view->get_transformed_node(), false); -+ -+ auto overlay = std::make_shared<wf::vswitch::vswitch_overlay_node_t>(view); -+ held_dim_overlay_node = -+ std::make_shared<wf::scene::translation_node_t>(); -+ held_dim_overlay_node->set_children_list({overlay}); -+ held_dim_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); -+ wf::scene::add_front(wf::get_core().scene(), held_dim_overlay_node); -+ -+ held_dim_view = view; -+ held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ wf::scene::damage_node(held_dim_overlay_node, -+ held_dim_overlay_node->get_bounding_box()); -+ } -+ - /** - * Check if we can switch the plugin capabilities. - * This makes sense only if the plugin is already active, otherwise, -@@ -486,12 +581,62 @@ - - bool add_direction(wf::point_t delta, wayfire_view view = nullptr) - { -- if (!is_active() && !start_switch()) -+ auto cws = output->wset()->get_current_workspace(); -+ auto grid = output->wset()->get_workspace_grid_size(); -+ auto target_ws = cws + delta; -+ target_ws.x = (target_ws.x % grid.width + grid.width) % grid.width; -+ target_ws.y = (target_ws.y % grid.height + grid.height) % grid.height; -+ auto animation_delta = target_ws - cws; -+ if (animation_delta.x > grid.width / 2) - { -- return false; -+ animation_delta.x -= grid.width; -+ } else if (animation_delta.x < -grid.width / 2) -+ { -+ animation_delta.x += grid.width; - } - -- if (view && ((view->role != wf::VIEW_ROLE_TOPLEVEL) || !view->is_mapped())) -+ if (animation_delta.y > grid.height / 2) -+ { -+ animation_delta.y -= grid.height; -+ } else if (animation_delta.y < -grid.height / 2) -+ { -+ animation_delta.y += grid.height; -+ } -+ -+ wayfire_toplevel_view fixed_view = nullptr; -+ if (view && (view->role == wf::VIEW_ROLE_TOPLEVEL) && view->is_mapped() && -+ (view->get_output() == output)) -+ { -+ fixed_view = toplevel_cast(view); -+ } -+ -+ if (!is_active()) -+ { -+ if (fixed_view) -+ { -+ clear_held_window_dim(); -+ wf::vswitch::move_view(fixed_view, target_ws); -+ output->wset()->set_workspace(target_ws); -+ wf::get_core().seat->focus_view(view); -+ -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = fixed_view; -+ output->emit(&ev); -+ return true; -+ } -+ -+ output->wset()->set_workspace(target_ws); -+ cube_workspace_animation_signal ev; -+ ev.delta = animation_delta; -+ ev.anchor_workspace = cws; -+ ev.fixed_view = nullptr; -+ output->emit(&ev); -+ return true; -+ } -+ -+ if (!fixed_view) - { - view = nullptr; - } -@@ -506,12 +651,47 @@ - wf::signal::connection_t<wf::view_disappeared_signal> on_grabbed_view_disappear = - [=] (wf::view_disappeared_signal *ev) - { -+ if (ev->view == held_dim_view) -+ { -+ clear_held_window_dim(); -+ } -+ - if (ev->view == algorithm->get_overlay_view()) - { - algorithm->set_overlay_view(nullptr); - } - }; - -+ wf::signal::connection_t<wf::input_event_signal<mwlr_keyboard_modifiers_event>> -+ on_modifier_change = -+ [=] (wf::input_event_signal<mwlr_keyboard_modifiers_event> *ev) -+ { -+ (void)ev; -+ auto modifiers = wf::get_core().seat->get_keyboard_modifiers(); -+ if ((modifiers & held_window_modifiers) == held_window_modifiers) -+ { -+ show_held_window_dim(get_held_window_target_view()); -+ } else if (held_dim_view) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ -+ wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_release = -+ [=] (wf::input_event_signal<wlr_keyboard_key_event> *ev) -+ { -+ if (!held_dim_view || (ev->event->state != WLR_KEY_RELEASED)) -+ { -+ return; -+ } -+ -+ auto mod = wf::get_core().seat->modifier_from_keycode(ev->event->keycode); -+ if (mod & held_dim_modifiers) -+ { -+ clear_held_window_dim(); -+ } -+ }; -+ - wf::signal::connection_t<wf::workspace_change_request_signal> on_set_workspace_request = - [=] (wf::workspace_change_request_signal *ev) - { -@@ -523,6 +703,15 @@ - return; - } - -+ if (!ev->fixed_views.empty()) -+ { -+ cube_workspace_animation_signal cube_ev; -+ cube_ev.delta = ev->new_viewport - ev->old_viewport; -+ cube_ev.anchor_workspace = ev->old_viewport; -+ cube_ev.fixed_view = ev->fixed_views[0]; -+ output->emit(&cube_ev); -+ } -+ - if (is_active()) - { - ev->carried_out = add_direction(ev->new_viewport - ev->old_viewport); -@@ -556,6 +745,7 @@ - - void fini() - { -+ clear_held_window_dim(); - if (is_active()) - { - algorithm->stop_switch(false); -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/plugins/vswitch/wayfire/plugins/vswitch.hpp b/plugins/vswitch/wayfire/plugins/vswitch.hpp ---- a/plugins/vswitch/wayfire/plugins/vswitch.hpp 2026-07-27 03:26:17.000000000 -0500 -+++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp 2026-08-16 19:07:42.962288689 -0500 -@@ -102,6 +102,8 @@ - SETUP4(with_win, get_target_view(), false); - SETUP4(send_win, get_target_view(), true); - -+ SETUP(with_win_modifier, 0, 0, get_target_view(), false); -+ - #undef SETUP4 - #undef SETUP - /* *INDENT-ON* */ -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/src/api/wayfire/workspace-stream.hpp b/src/api/wayfire/workspace-stream.hpp ---- a/src/api/wayfire/workspace-stream.hpp 2026-07-27 03:26:17.000000000 -0500 -+++ b/src/api/wayfire/workspace-stream.hpp 2026-08-16 19:07:42.963288695 -0500 -@@ -21,6 +21,14 @@ - // of Wayfire is used. - std::optional<wf::color_t> background; - -+ // Whether desktop environment views, such as wallpapers and panels, -+ // should be included in the workspace stream. -+ bool include_desktop_environment = true; -+ -+ // Whether regular workspace views should be included in the workspace -+ // stream. -+ bool include_workspace_views = true; -+ - wf::output_t*const output; - const wf::point_t ws; - -diff -ruN '--exclude=*.orig' '--exclude=*.rej' '--exclude=build' a/src/output/workspace-stream.cpp b/src/output/workspace-stream.cpp ---- a/src/output/workspace-stream.cpp 2026-07-27 03:26:17.000000000 -0500 -+++ b/src/output/workspace-stream.cpp 2026-08-16 19:07:42.963288695 -0500 -@@ -56,6 +56,16 @@ - const bool is_de = (view && (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)); - size_t num_generated = this->instances.size(); - -+ if (is_de && !self->include_desktop_environment) -+ { -+ continue; -+ } -+ -+ if (!is_de && !self->include_workspace_views) -+ { -+ continue; -+ } -+ - // We push the damage as-is for desktop environment views, because they are visible on - // every workspace. - ch->gen_render_instances(this->instances, diff --git a/gui-wm/wayfire/files/wayfire-session b/gui-wm/wayfire/files/wayfire-session deleted file mode 100644 index 8267ea0e11fc..000000000000 --- a/gui-wm/wayfire/files/wayfire-session +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -# output to syslog and stderr -# with PID and syslog identity 'wayfire' -exec 1> >(logger --id=$$ --stderr --tag wayfire) 2>&1 - -# if undefined, or empty, set to freedesktop specification defaults -[ -z "$XDG_DATA_DIRS" ] && export XDG_DATA_DIRS="/usr/local/share:/usr/share" - -# freedesktop specifications mandate that the definition -# of XDG_SESSION_TYPE should be respected -export XDG_SESSION_TYPE=wayland -export GDK_BACKEND=wayland -export QT_QPA_PLATFORM=wayland -export MOZ_ENABLE_WAYLAND=1 - -exec @EPREFIX@/usr/bin/wayfire "$@" diff --git a/gui-wm/wayfire/files/wayfire-session-2 b/gui-wm/wayfire/files/wayfire-session-2 deleted file mode 100644 index 0713ca0bbb93..000000000000 --- a/gui-wm/wayfire/files/wayfire-session-2 +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -# output to syslog and stderr -# with PID and syslog identity 'wayfire' -exec 1> >(logger --id=$$ --stderr --tag wayfire) 2>&1 - -# if undefined, or empty, set to freedesktop specification defaults -[ -z "$XDG_DATA_DIRS" ] && export XDG_DATA_DIRS="/usr/local/share:/usr/share" - -# freedesktop specifications mandate that the definition -# of XDG_SESSION_TYPE should be respected -export XDG_SESSION_TYPE=wayland -export GDK_BACKEND=wayland -export QT_QPA_PLATFORM=wayland -export MOZ_ENABLE_WAYLAND=1 - -# copy wayfire.ini to $HOME/.config/ if not already there -configdir="${XDG_CONFIG_HOME:=${HOME}/.config}" -if [[ ! -f ${configdir}/wayfire.ini ]]; then - cp /usr/share/wayfire/wayfire.ini "${configdir}" -fi - -# override or add environment variables -[[ -f @EPREFIX@/etc/wayfire.env ]] && . @EPREFIX@/etc/wayfire.env - -exec @DBUS_RUN_SESSION@ @EPREFIX@/usr/bin/wayfire "$@" diff --git a/gui-wm/wayfire/files/wayfire-session.desktop b/gui-wm/wayfire/files/wayfire-session.desktop deleted file mode 100644 index 1088c4e0b793..000000000000 --- a/gui-wm/wayfire/files/wayfire-session.desktop +++ /dev/null @@ -1,7 +0,0 @@ -[Desktop Entry] -Name=Wayfire Session -Comment=wayfire session with proper env vars -Exec=@EPREFIX@/usr/bin/wayfire-session $* -TryExec=@EPREFIX@/usr/bin/wayfire-session -Type=Application -DesktopNames=Wayfire diff --git a/gui-wm/wayfire/files/wayfire.env b/gui-wm/wayfire/files/wayfire.env deleted file mode 100644 index 47ce61e3183c..000000000000 --- a/gui-wm/wayfire/files/wayfire.env +++ /dev/null @@ -1,2 +0,0 @@ -# Uncomment if the mouse cursor is invisible: -#export WLR_NO_HARDWARE_CURSORS=1 diff --git a/gui-wm/wayfire/files/windowrules.patch b/gui-wm/wayfire/files/windowrules.patch deleted file mode 100644 index 9d5cfef3bf7c..000000000000 --- a/gui-wm/wayfire/files/windowrules.patch +++ /dev/null @@ -1,188 +0,0 @@ -diff --git a/plugins/window-rules/view-action-interface.cpp b/plugins/window-rules/view-action-interface.cpp -index eb973280..839c9fb5 100644 ---- a/plugins/window-rules/view-action-interface.cpp -+++ b/plugins/window-rules/view-action-interface.cpp -@@ -6,6 +6,7 @@ - #include "wayfire/plugins/grid.hpp" - #include "wayfire/util/log.hpp" - #include "wayfire/view-transform.hpp" -+#include <wayfire/signal-definitions.hpp> - - #include <algorithm> - #include <cfloat> -@@ -16,7 +17,9 @@ - namespace wf - { - view_action_interface_t::~view_action_interface_t() --{} -+{ -+ _view->erase_data("wm-actions-above"); -+} - - bool view_action_interface_t::execute(const std::string & name, - const std::vector<variant_t> & args) -@@ -173,6 +176,14 @@ bool view_action_interface_t::execute(const std::string & name, - } - - return true; -+ } else if (name == "sticky") -+ { -+ _make_sticky(); -+ return false; -+ } else if (name == "always_on_top") -+ { -+ _always_on_top(); -+ return false; - } - - LOGE("View action interface: Unsupported action execution requested. Name: ", -@@ -186,6 +197,11 @@ void view_action_interface_t::set_view(wayfire_view view) - _view = view; - } - -+void view_action_interface_t::set_above_layer(nonstd::observer_ptr<wf::sublayer_t> always_above) -+{ -+ _always_above = always_above; -+} -+ - void view_action_interface_t::_maximize() - { - _view->tile_request(wf::TILED_EDGES_ALL); -@@ -206,6 +222,41 @@ void view_action_interface_t::_unminimize() - _view->set_minimized(false); - } - -+void view_action_interface_t::_make_sticky() -+{ -+ _view->set_sticky(1); -+} -+ -+void view_action_interface_t::_always_on_top() -+{ -+ if (!_view) -+ { -+ return; -+ } -+ -+ auto output = _view->get_output(); -+ if (!output) -+ { -+ return; -+ } -+ -+ if (_view->has_data("wm-actions-above")) -+ { -+ output->workspace->add_view(_view, -+ (wf::layer_t)output->workspace->get_view_layer(_view)); -+ _view->erase_data("wm-actions-above"); -+ } else -+ { -+ output->workspace->add_view_to_sublayer(_view, _always_above); -+ _view->store_data(std::make_unique<wf::custom_data_t>(), -+ "wm-actions-above"); -+ } -+ -+ wf::_view_signal data; -+ data.view = _view; -+ output->emit_signal("wm-actions-above-changed", &data); -+} -+ - std::tuple<bool, float> view_action_interface_t::_expect_float( - const std::vector<variant_t> & args, std::size_t position) - { -diff --git a/plugins/window-rules/view-action-interface.hpp b/plugins/window-rules/view-action-interface.hpp -index 26c34bc7..0f23607d 100644 ---- a/plugins/window-rules/view-action-interface.hpp -+++ b/plugins/window-rules/view-action-interface.hpp -@@ -3,6 +3,7 @@ - - #include "wayfire/action/action_interface.hpp" - #include "wayfire/view.hpp" -+#include <wayfire/workspace-manager.hpp> - #include <string> - #include <tuple> - #include <vector> -@@ -18,12 +19,15 @@ class view_action_interface_t : public action_interface_t - const std::vector<variant_t> & args) override; - - void set_view(wayfire_view view); -+ void set_above_layer(nonstd::observer_ptr<wf::sublayer_t> always_above); - - private: - void _maximize(); - void _unmaximize(); - void _minimize(); - void _unminimize(); -+ void _make_sticky(); -+ void _always_on_top(); - - std::tuple<bool, float> _expect_float(const std::vector<variant_t> & args, - std::size_t position); -@@ -51,6 +55,7 @@ class view_action_interface_t : public action_interface_t - wf::geometry_t _get_workspace_grid_geometry(wf::output_t *output) const; - - wayfire_view _view; -+ nonstd::observer_ptr<wf::sublayer_t> _always_above; - }; - } // End namespace wf. - -diff --git a/plugins/window-rules/window-rules.cpp b/plugins/window-rules/window-rules.cpp -index 295dd798..723b2f16 100644 ---- a/plugins/window-rules/window-rules.cpp -+++ b/plugins/window-rules/window-rules.cpp -@@ -26,6 +26,7 @@ class wayfire_window_rules_t : public wf::plugin_interface_t - void apply(const std::string & signal, wf::signal_data_t *data); - - private: -+ nonstd::observer_ptr<wf::sublayer_t> always_above; - void setup_rules_from_config(); - wf::lexer_t _lexer; - -@@ -87,6 +88,9 @@ void wayfire_window_rules_t::init() - output->connect_signal("view-minimized", &_minimized); - output->connect_signal("view-fullscreen", &_fullscreened); - wf::get_core().connect_signal("reload-config", &_reload_config); -+ -+ always_above = output->workspace->create_sublayer( -+ wf::LAYER_WORKSPACE, wf::SUBLAYER_DOCKED_ABOVE); - } - - void wayfire_window_rules_t::fini() -@@ -96,6 +100,15 @@ void wayfire_window_rules_t::fini() - { - wf::get_core().erase_data<wf::lambda_rules_registrations_t>(); - } -+ auto always_on_top_views = -+ output->workspace->get_views_in_sublayer(always_above); -+ -+ for (auto view : always_on_top_views) -+ { -+ view->erase_data("wm-actions-above"); -+ } -+ -+ output->workspace->destroy_sublayer(always_above); - } - - void wayfire_window_rules_t::apply(const std::string & signal, -@@ -128,6 +141,7 @@ void wayfire_window_rules_t::apply(const std::string & signal, - { - _access_interface.set_view(view); - _action_interface.set_view(view); -+ _action_interface.set_above_layer(always_above); - auto error = rule->apply(signal, _access_interface, _action_interface); - if (error) - { -@@ -200,10 +214,11 @@ void wayfire_window_rules_t::setup_rules_from_config() - { - _lexer.reset(opt->get_value_str()); - auto rule = wf::rule_parser_t().parse(_lexer); -- if (rule != nullptr) -+ if (!rule) - { -- _rules.push_back(rule); -+ continue; - } -+ _rules.push_back(rule); - } - } - diff --git a/gui-wm/wayfire/graphify-out/.graphify_labels.json b/gui-wm/wayfire/graphify-out/.graphify_labels.json deleted file mode 100644 index 4ddd22536126..000000000000 --- a/gui-wm/wayfire/graphify-out/.graphify_labels.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "0": "Transparent cube animation compatibility notes", - "1": "wayfire-session", - "2": "wayfire-session-2", - "3": "render_test_t", - "4": "redraw-check.py", - "5": "wayfire-0.11.0-cube-rendering-notes.md", - "6": "map", - "7": "string", - "8": "README.md", - "9": "run.py" -} diff --git a/gui-wm/wayfire/graphify-out/.graphify_labels.json.sig b/gui-wm/wayfire/graphify-out/.graphify_labels.json.sig deleted file mode 100644 index 311fb262e861..000000000000 --- a/gui-wm/wayfire/graphify-out/.graphify_labels.json.sig +++ /dev/null @@ -1 +0,0 @@ -{"0": "727e2dbd5ce75a9a", "1": "72d95ee36dfadcc3", "2": "0b11d95a89614162", "3": "dbeaea3fe899ab99", "4": "fd16686c6ba05bdd", "5": "09edd7b1150fd0e2", "6": "68858be981e3a1ba", "7": "7ffe7cfee8e7029d", "8": "b2f736171e800e0b", "9": "59c444c719911f24"}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/.graphify_root b/gui-wm/wayfire/graphify-out/.graphify_root deleted file mode 100644 index 945c9b46d684..000000000000 --- a/gui-wm/wayfire/graphify-out/.graphify_root +++ /dev/null @@ -1 +0,0 @@ -.
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/2026-09-18/.graphify_labels.json b/gui-wm/wayfire/graphify-out/2026-09-18/.graphify_labels.json deleted file mode 100644 index 09f5e129f82b..000000000000 --- a/gui-wm/wayfire/graphify-out/2026-09-18/.graphify_labels.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "0": "Transparent cube animation compatibility notes", - "1": "wayfire-session", - "2": "wayfire-session-2" -} diff --git a/gui-wm/wayfire/graphify-out/2026-09-18/GRAPH_REPORT.md b/gui-wm/wayfire/graphify-out/2026-09-18/GRAPH_REPORT.md deleted file mode 100644 index b129cb535a49..000000000000 --- a/gui-wm/wayfire/graphify-out/2026-09-18/GRAPH_REPORT.md +++ /dev/null @@ -1,53 +0,0 @@ -# Graph Report - wayfire (2026-09-18) - -## Corpus Check -- 3 files · ~615 words -- Verdict: corpus is large enough that graph structure adds value. - -## Summary -- 19 nodes · 16 edges · 3 communities -- Extraction: 100% EXTRACTED · 0% INFERRED · 0% AMBIGUOUS -- Token cost: 0 input · 0 output - -## Community Hubs (Navigation) -- Transparent cube animation compatibility notes -- wayfire-session -- wayfire-session-2 - -## God Nodes (most connected - your core abstractions) -1. `Transparent cube animation compatibility notes` - 6 edges -2. `Goal` - 1 edges -3. `Working theory` - 1 edges -4. `Minimum target` - 1 edges -5. `Current Direction` - 1 edges -6. `Avoid for now` - 1 edges - -## Surprising Connections (you probably didn't know these) -- None detected - all connections are within the same source files. - -## Import Cycles -- None detected. - -## Communities (3 total, 0 thin omitted) - -### Community 0 - "Transparent cube animation compatibility notes" -Cohesion: 0.29 -Nodes (6): Avoid for now, Current Direction, Goal, Minimum target, Transparent cube animation compatibility notes, Working theory - -### Community 1 - "wayfire-session" -Cohesion: 0.33 -Nodes (5): wayfire-session script, GDK_BACKEND, MOZ_ENABLE_WAYLAND, QT_QPA_PLATFORM, XDG_SESSION_TYPE - -### Community 2 - "wayfire-session-2" -Cohesion: 0.33 -Nodes (5): wayfire-session-2 script, GDK_BACKEND, MOZ_ENABLE_WAYLAND, QT_QPA_PLATFORM, XDG_SESSION_TYPE - -## Knowledge Gaps -- **5 isolated node(s):** `Goal`, `Working theory`, `Minimum target`, `Current Direction`, `Avoid for now` - These have ≤1 connection - possible missing edges or undocumented components. (Counts symbols only; 16 node(s) total have ≤1 connection when file, concept and rationale nodes are included.) - -## Suggested Questions -_Questions this graph is uniquely positioned to answer:_ - -- **What connects `Goal`, `Working theory`, `Minimum target` to the rest of the system?** - _5 weakly-connected nodes found - possible documentation gaps or missing edges._
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/2026-09-18/graph.json b/gui-wm/wayfire/graphify-out/2026-09-18/graph.json deleted file mode 100644 index 71f699fd9e94..000000000000 --- a/gui-wm/wayfire/graphify-out/2026-09-18/graph.json +++ /dev/null @@ -1,450 +0,0 @@ -{ - "directed": false, - "multigraph": false, - "graph": {}, - "nodes": [ - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes", - "label": "wayfire-0.10.1-transparent-cube-animation-notes.md", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "page", - "norm_label": "wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L1" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes_avoid_for_now", - "label": "Avoid for now", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "heading", - "norm_label": "avoid for now", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L65" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes_current_direction", - "label": "Current Direction", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "heading", - "norm_label": "current direction", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L55" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes_goal", - "label": "Goal", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "heading", - "norm_label": "goal", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L3" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes_minimum_target", - "label": "Minimum target", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "heading", - "norm_label": "minimum target", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L27" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "label": "Transparent cube animation compatibility notes", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "heading", - "norm_label": "transparent cube animation compatibility notes", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L1" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes_working_theory", - "label": "Working theory", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "heading", - "norm_label": "working theory", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L14" - }, - { - "id": "files_wayfire_session", - "label": "wayfire-session", - "_origin": "ast", - "community": 1, - "community_name": "wayfire-session", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "file" - }, - "norm_label": "wayfire-session", - "source_file": "files/wayfire-session", - "source_location": "L1" - }, - { - "id": "files_wayfire_session__entry", - "label": "wayfire-session script", - "_origin": "ast", - "community": 1, - "community_name": "wayfire-session", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "bash_entrypoint" - }, - "norm_label": "wayfire-session script", - "source_file": "files/wayfire-session", - "source_location": "L1" - }, - { - "id": "files_wayfire_session_gdk_backend", - "label": "GDK_BACKEND", - "_origin": "ast", - "community": 1, - "community_name": "wayfire-session", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "gdk_backend", - "source_file": "files/wayfire-session", - "source_location": "L13" - }, - { - "id": "files_wayfire_session_moz_enable_wayland", - "label": "MOZ_ENABLE_WAYLAND", - "_origin": "ast", - "community": 1, - "community_name": "wayfire-session", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "moz_enable_wayland", - "source_file": "files/wayfire-session", - "source_location": "L15" - }, - { - "id": "files_wayfire_session_qt_qpa_platform", - "label": "QT_QPA_PLATFORM", - "_origin": "ast", - "community": 1, - "community_name": "wayfire-session", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "qt_qpa_platform", - "source_file": "files/wayfire-session", - "source_location": "L14" - }, - { - "id": "files_wayfire_session_xdg_session_type", - "label": "XDG_SESSION_TYPE", - "_origin": "ast", - "community": 1, - "community_name": "wayfire-session", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "xdg_session_type", - "source_file": "files/wayfire-session", - "source_location": "L12" - }, - { - "id": "files_wayfire_session_2", - "label": "wayfire-session-2", - "_origin": "ast", - "community": 2, - "community_name": "wayfire-session-2", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "file" - }, - "norm_label": "wayfire-session-2", - "source_file": "files/wayfire-session-2", - "source_location": "L1" - }, - { - "id": "files_wayfire_session_2__entry", - "label": "wayfire-session-2 script", - "_origin": "ast", - "community": 2, - "community_name": "wayfire-session-2", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "bash_entrypoint" - }, - "norm_label": "wayfire-session-2 script", - "source_file": "files/wayfire-session-2", - "source_location": "L1" - }, - { - "id": "files_wayfire_session_2_gdk_backend", - "label": "GDK_BACKEND", - "_origin": "ast", - "community": 2, - "community_name": "wayfire-session-2", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "gdk_backend", - "source_file": "files/wayfire-session-2", - "source_location": "L13" - }, - { - "id": "files_wayfire_session_2_moz_enable_wayland", - "label": "MOZ_ENABLE_WAYLAND", - "_origin": "ast", - "community": 2, - "community_name": "wayfire-session-2", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "moz_enable_wayland", - "source_file": "files/wayfire-session-2", - "source_location": "L15" - }, - { - "id": "files_wayfire_session_2_qt_qpa_platform", - "label": "QT_QPA_PLATFORM", - "_origin": "ast", - "community": 2, - "community_name": "wayfire-session-2", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "qt_qpa_platform", - "source_file": "files/wayfire-session-2", - "source_location": "L14" - }, - { - "id": "files_wayfire_session_2_xdg_session_type", - "label": "XDG_SESSION_TYPE", - "_origin": "ast", - "community": 2, - "community_name": "wayfire-session-2", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "xdg_session_type", - "source_file": "files/wayfire-session-2", - "source_location": "L12" - } - ], - "links": [ - { - "source": "files_wayfire_0_10_1_transparent_cube_animation_notes", - "target": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L1", - "weight": 1.0 - }, - { - "source": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "target": "files_wayfire_0_10_1_transparent_cube_animation_notes_working_theory", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L14", - "weight": 1.0 - }, - { - "source": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "target": "files_wayfire_0_10_1_transparent_cube_animation_notes_minimum_target", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L27", - "weight": 1.0 - }, - { - "source": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "target": "files_wayfire_0_10_1_transparent_cube_animation_notes_goal", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L3", - "weight": 1.0 - }, - { - "source": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "target": "files_wayfire_0_10_1_transparent_cube_animation_notes_current_direction", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L55", - "weight": 1.0 - }, - { - "source": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "target": "files_wayfire_0_10_1_transparent_cube_animation_notes_avoid_for_now", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L65", - "weight": 1.0 - }, - { - "source": "files_wayfire_session", - "target": "files_wayfire_session__entry", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session", - "source_location": "L1", - "weight": 1.0 - }, - { - "source": "files_wayfire_session_2", - "target": "files_wayfire_session_2__entry", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session-2", - "source_location": "L1", - "weight": 1.0 - }, - { - "source": "files_wayfire_session", - "target": "files_wayfire_session_xdg_session_type", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session", - "source_location": "L12", - "weight": 1.0 - }, - { - "source": "files_wayfire_session", - "target": "files_wayfire_session_gdk_backend", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session", - "source_location": "L13", - "weight": 1.0 - }, - { - "source": "files_wayfire_session", - "target": "files_wayfire_session_qt_qpa_platform", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session", - "source_location": "L14", - "weight": 1.0 - }, - { - "source": "files_wayfire_session", - "target": "files_wayfire_session_moz_enable_wayland", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session", - "source_location": "L15", - "weight": 1.0 - }, - { - "source": "files_wayfire_session_2", - "target": "files_wayfire_session_2_xdg_session_type", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session-2", - "source_location": "L12", - "weight": 1.0 - }, - { - "source": "files_wayfire_session_2", - "target": "files_wayfire_session_2_gdk_backend", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session-2", - "source_location": "L13", - "weight": 1.0 - }, - { - "source": "files_wayfire_session_2", - "target": "files_wayfire_session_2_qt_qpa_platform", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session-2", - "source_location": "L14", - "weight": 1.0 - }, - { - "source": "files_wayfire_session_2", - "target": "files_wayfire_session_2_moz_enable_wayland", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session-2", - "source_location": "L15", - "weight": 1.0 - } - ], - "hyperedges": [] -}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/2026-09-18/manifest.json b/gui-wm/wayfire/graphify-out/2026-09-18/manifest.json deleted file mode 100644 index 9e82959e95c9..000000000000 --- a/gui-wm/wayfire/graphify-out/2026-09-18/manifest.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "files/wayfire-session": { - "mtime": 1653360147.3630395, - "seen": 1789770744.5676844, - "ast_hash": "b3aeec97b74efdb33552772c7f04b00f", - "semantic_hash": "" - }, - "files/wayfire-session-2": { - "mtime": 1780641764.4558036, - "seen": 1789770744.5676875, - "ast_hash": "0b8e4dd2101110c3e6a3688b8b035251", - "semantic_hash": "" - }, - "files/wayfire-0.10.1-transparent-cube-animation-notes.md": { - "mtime": 1780715444.271489, - "seen": 1789770744.567689, - "ast_hash": "437c539443e80b8f7c072aff6a0bd371", - "semantic_hash": "" - } -}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/GRAPH_REPORT.md b/gui-wm/wayfire/graphify-out/GRAPH_REPORT.md deleted file mode 100644 index 5b654b65e6f0..000000000000 --- a/gui-wm/wayfire/graphify-out/GRAPH_REPORT.md +++ /dev/null @@ -1,73 +0,0 @@ -# Graph Report - wayfire (2026-09-18) - -## Corpus Check -- 10 files · ~2,248 words -- Verdict: corpus is large enough that graph structure adds value. - -## Summary -- 49 nodes · 46 edges · 10 communities (4 shown, 5 thin omitted) -- Extraction: 100% EXTRACTED · 0% INFERRED · 0% AMBIGUOUS -- Token cost: 0 input · 0 output - -## Community Hubs (Navigation) -- Transparent cube animation compatibility notes -- wayfire-session -- wayfire-session-2 -- render_test_t -- redraw-check.py -- wayfire-0.11.0-cube-rendering-notes.md -- map -- string -- README.md - -## God Nodes (most connected - your core abstractions) -1. `render_test_t` - 17 edges -2. `Transparent cube animation compatibility notes` - 6 edges -3. `ipc()` - 4 edges -4. `solid()` - 2 edges -5. `repo` - 1 edges -6. `rectangle` - 1 edges -7. `solids` - 1 edges -8. `passes` - 1 edges -9. `count_passes` - 1 edges -10. `Goal` - 1 edges - -## Surprising Connections (you probably didn't know these) -- `solid()` --calls--> `ipc()` [EXTRACTED] - tests/render-regression/redraw-check.py → tests/render-regression/ipc.py - -## Import Cycles -- None detected. - -## Communities (10 total, 5 thin omitted) - -### Community 0 - "Transparent cube animation compatibility notes" -Cohesion: 0.29 -Nodes (6): Avoid for now, Current Direction, Goal, Minimum target, Transparent cube animation compatibility notes, Working theory - -### Community 1 - "wayfire-session" -Cohesion: 0.33 -Nodes (5): wayfire-session script, GDK_BACKEND, MOZ_ENABLE_WAYLAND, QT_QPA_PLATFORM, XDG_SESSION_TYPE - -### Community 2 - "wayfire-session-2" -Cohesion: 0.33 -Nodes (5): wayfire-session-2 script, GDK_BACKEND, MOZ_ENABLE_WAYLAND, QT_QPA_PLATFORM, XDG_SESSION_TYPE - -### Community 3 - "render_test_t" -Cohesion: 0.14 -Nodes (13): color_rect_view_t, connection_t, method_repository_t, plugin_interface_t, ref_ptr_t, render_pass_begin_signal, shared_ptr, render_test_t (+5 more) - -## Knowledge Gaps -- **12 isolated node(s):** `repo`, `rectangle`, `solids`, `passes`, `count_passes` (+7 more) - These have ≤1 connection - possible missing edges or undocumented components. (Counts symbols only; 36 node(s) total have ≤1 connection when file, concept and rationale nodes are included.) -- **5 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes. - -## Suggested Questions -_Questions this graph is uniquely positioned to answer:_ - -- **Why does `render_test_t` connect `render_test_t` to `map`, `string`?** - _High betweenness centrality (0.119) - this node is a cross-community bridge._ -- **What connects `repo`, `rectangle`, `solids` to the rest of the system?** - _12 weakly-connected nodes found - possible documentation gaps or missing edges._ -- **Should `render_test_t` be split into smaller, more focused modules?** - _Cohesion score 0.14285714285714285 - nodes in this community are weakly interconnected._
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/05eafb6a3d31ffc3daa8668c07dd7a92234e8056dfbaf507b2d06c4c0ce29adc.json b/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/05eafb6a3d31ffc3daa8668c07dd7a92234e8056dfbaf507b2d06c4c0ce29adc.json deleted file mode 100644 index faad047a2370..000000000000 --- a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/05eafb6a3d31ffc3daa8668c07dd7a92234e8056dfbaf507b2d06c4c0ce29adc.json +++ /dev/null @@ -1 +0,0 @@ -{"nodes": [{"id": "$graphify-root$_tests_render_regression_ipc_py", "label": "ipc.py", "file_type": "code", "source_file": "tests/render-regression/ipc.py", "source_location": "L1"}, {"id": "$graphify-root$_tests_render_regression_ipc_ipc", "label": "ipc()", "file_type": "code", "source_file": "tests/render-regression/ipc.py", "source_location": "L9", "_callable": true}], "edges": [{"source": "$graphify-root$_tests_render_regression_ipc_py", "target": "json", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/ipc.py", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_ipc_py", "target": "os", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/ipc.py", "source_location": "L2", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_ipc_py", "target": "socket", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/ipc.py", "source_location": "L3", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_ipc_py", "target": "struct", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/ipc.py", "source_location": "L4", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_ipc_py", "target": "pathlib", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/ipc.py", "source_location": "L5", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_ipc_py", "target": "$graphify-root$_tests_render_regression_ipc_ipc", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/render-regression/ipc.py", "source_location": "L9", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_ipc_py", "target": "sys", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/ipc.py", "source_location": "L26", "weight": 1.0}], "raw_calls": [{"caller_nid": "$graphify-root$_tests_render_regression_ipc_ipc", "callee": "socket", "is_member_call": true, "source_file": "tests/render-regression/ipc.py", "source_location": "L10", "receiver": "socket"}, {"caller_nid": "$graphify-root$_tests_render_regression_ipc_ipc", "callee": "settimeout", "is_member_call": true, "source_file": "tests/render-regression/ipc.py", "source_location": "L11", "receiver": "sock"}, {"caller_nid": "$graphify-root$_tests_render_regression_ipc_ipc", "callee": "connect", "is_member_call": true, "source_file": "tests/render-regression/ipc.py", "source_location": "L12", "receiver": "sock"}, {"caller_nid": "$graphify-root$_tests_render_regression_ipc_ipc", "callee": "encode", "is_member_call": true, "source_file": "tests/render-regression/ipc.py", "source_location": "L13", "receiver": null}, {"caller_nid": "$graphify-root$_tests_render_regression_ipc_ipc", "callee": "dumps", "is_member_call": true, "source_file": "tests/render-regression/ipc.py", "source_location": "L13", "receiver": "json"}, {"caller_nid": "$graphify-root$_tests_render_regression_ipc_ipc", "callee": "sendall", "is_member_call": true, "source_file": "tests/render-regression/ipc.py", "source_location": "L14", "receiver": "sock"}, {"caller_nid": "$graphify-root$_tests_render_regression_ipc_ipc", "callee": "pack", "is_member_call": true, "source_file": "tests/render-regression/ipc.py", "source_location": "L14", "receiver": "struct"}, {"caller_nid": "$graphify-root$_tests_render_regression_ipc_ipc", "callee": "loads", "is_member_call": true, "source_file": "tests/render-regression/ipc.py", "source_location": "L23", "receiver": "json"}, {"caller_nid": "$graphify-root$_tests_render_regression_ipc_ipc", "callee": "read", "is_member_call": false, "source_file": "tests/render-regression/ipc.py", "source_location": "L23", "receiver": null}, {"caller_nid": "$graphify-root$_tests_render_regression_ipc_ipc", "callee": "unpack", "is_member_call": true, "source_file": "tests/render-regression/ipc.py", "source_location": "L23", "receiver": "struct"}, {"caller_nid": "$graphify-root$_tests_render_regression_ipc_ipc", "callee": "read", "is_member_call": false, "source_file": "tests/render-regression/ipc.py", "source_location": "L23", "receiver": null}]}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/16b475fe1869b41ccaca5639bc87dfc15fe69c3ffe5edf9d9fd2b7e7b6985901.json b/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/16b475fe1869b41ccaca5639bc87dfc15fe69c3ffe5edf9d9fd2b7e7b6985901.json deleted file mode 100644 index 013c70120c4c..000000000000 --- a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/16b475fe1869b41ccaca5639bc87dfc15fe69c3ffe5edf9d9fd2b7e7b6985901.json +++ /dev/null @@ -1 +0,0 @@ -{"nodes": [{"id": "$graphify-root$_files_wayfire_0_11_0_cube_rendering_notes_md", "label": "wayfire-0.11.0-cube-rendering-notes.md", "file_type": "document", "node_kind": "page", "source_file": "files/wayfire-0.11.0-cube-rendering-notes.md", "source_location": "L1"}, {"id": "$graphify-root$_files_wayfire_0_11_0_cube_rendering_notes_cube_damage_and_layer_ordering_revision_3", "label": "Cube damage and layer ordering, revision 3", "file_type": "document", "node_kind": "heading", "source_file": "files/wayfire-0.11.0-cube-rendering-notes.md", "source_location": "L1"}], "edges": [{"source": "$graphify-root$_files_wayfire_0_11_0_cube_rendering_notes_md", "target": "$graphify-root$_files_wayfire_0_11_0_cube_rendering_notes_cube_damage_and_layer_ordering_revision_3", "relation": "contains", "confidence": "EXTRACTED", "source_file": "files/wayfire-0.11.0-cube-rendering-notes.md", "source_location": "L1", "weight": 1.0}], "input_tokens": 0, "output_tokens": 0}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/3043830d9822a41238110bbd2ec25f6ef2931793959d60403d4a33ce3aa5e684.json b/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/3043830d9822a41238110bbd2ec25f6ef2931793959d60403d4a33ce3aa5e684.json deleted file mode 100644 index 80813a4a2f6a..000000000000 --- a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/3043830d9822a41238110bbd2ec25f6ef2931793959d60403d4a33ce3aa5e684.json +++ /dev/null @@ -1 +0,0 @@ -{"nodes": [{"id": "$graphify-root$_tests_render_regression_render_test_cpp", "label": "render-test.cpp", "file_type": "code", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L1"}, {"id": "$graphify-root$_tests_render_regression_render_test_render_test_t", "label": "render_test_t", "file_type": "code", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L19", "_callable": true, "_callable_class": true}, {"id": "plugin_interface_t", "label": "plugin_interface_t", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "$graphify-root$/tests/render-regression/render-test.cpp"}, {"id": "ref_ptr_t", "label": "ref_ptr_t", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "$graphify-root$/tests/render-regression/render-test.cpp"}, {"id": "method_repository_t", "label": "method_repository_t", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "$graphify-root$/tests/render-regression/render-test.cpp"}, {"id": "$graphify-root$_tests_render_regression_render_test_render_test_t_repo", "label": "repo", "file_type": "code", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L21"}, {"id": "shared_ptr", "label": "shared_ptr", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "$graphify-root$/tests/render-regression/render-test.cpp"}, {"id": "color_rect_view_t", "label": "color_rect_view_t", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "$graphify-root$/tests/render-regression/render-test.cpp"}, {"id": "$graphify-root$_tests_render_regression_render_test_render_test_t_rectangle", "label": "rectangle", "file_type": "code", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L22"}, {"id": "map", "label": "map", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "$graphify-root$/tests/render-regression/render-test.cpp"}, {"id": "string", "label": "string", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "$graphify-root$/tests/render-regression/render-test.cpp"}, {"id": "$graphify-root$_tests_render_regression_render_test_render_test_t_solids", "label": "solids", "file_type": "code", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L23"}, {"id": "$graphify-root$_tests_render_regression_render_test_render_test_t_passes", "label": "passes", "file_type": "code", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L24"}, {"id": "connection_t", "label": "connection_t", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "$graphify-root$/tests/render-regression/render-test.cpp"}, {"id": "render_pass_begin_signal", "label": "render_pass_begin_signal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "$graphify-root$/tests/render-regression/render-test.cpp"}, {"id": "$graphify-root$_tests_render_regression_render_test_render_test_t_count_passes", "label": "count_passes", "file_type": "code", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L25"}, {"id": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "label": ".init()", "file_type": "code", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L28", "_callable": true}, {"id": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "label": ".fini()", "file_type": "code", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L173", "_callable": true}], "edges": [{"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "core", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "output", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L2", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "output_layout", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L3", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "plugin", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L4", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "compositor_view", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L5", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "render_manager", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L6", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "wlroots_full", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L7", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "drm_fourcc", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L8", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "fstream", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L9", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "map", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L10", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "scene_operations", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L11", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "scene_render", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L12", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "workspace_set", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L13", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "opengl", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L14", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "shared_core_data", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L15", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "ipc_method_repository", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L16", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "cube_control_signal", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L17", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_cpp", "target": "$graphify-root$_tests_render_regression_render_test_render_test_t", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L19", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "plugin_interface_t", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L19", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "ref_ptr_t", "relation": "references", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L21", "weight": 1.0, "context": "field"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "method_repository_t", "relation": "references", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L21", "weight": 1.0, "context": "generic_arg"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "$graphify-root$_tests_render_regression_render_test_render_test_t_repo", "relation": "defines", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L21", "weight": 1.0, "context": "field"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "shared_ptr", "relation": "references", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L22", "weight": 1.0, "context": "field"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "color_rect_view_t", "relation": "references", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L22", "weight": 1.0, "context": "generic_arg"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "$graphify-root$_tests_render_regression_render_test_render_test_t_rectangle", "relation": "defines", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L22", "weight": 1.0, "context": "field"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "map", "relation": "references", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L23", "weight": 1.0, "context": "field"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "string", "relation": "references", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L23", "weight": 1.0, "context": "generic_arg"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "shared_ptr", "relation": "references", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L23", "weight": 1.0, "context": "generic_arg"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "color_rect_view_t", "relation": "references", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L23", "weight": 1.0, "context": "generic_arg"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "$graphify-root$_tests_render_regression_render_test_render_test_t_solids", "relation": "defines", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L23", "weight": 1.0, "context": "field"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "$graphify-root$_tests_render_regression_render_test_render_test_t_passes", "relation": "defines", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L24", "weight": 1.0, "context": "field"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "connection_t", "relation": "references", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L25", "weight": 1.0, "context": "field"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "render_pass_begin_signal", "relation": "references", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L25", "weight": 1.0, "context": "generic_arg"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "$graphify-root$_tests_render_regression_render_test_render_test_t_count_passes", "relation": "defines", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L25", "weight": 1.0, "context": "field"}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "relation": "method", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L28", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t", "target": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "relation": "method", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L173", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "target": "string", "relation": "calls", "context": "call", "confidence": "EXTRACTED", "source_file": "tests/render-regression/render-test.cpp", "source_location": "L33", "weight": 1.0}], "raw_calls": [{"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "connect", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L30", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_core", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L30", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "register_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L31", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "find_output", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L33", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_core", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L33", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_layout_geometry", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L34", "receiver": "output", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "allocate", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L36", "receiver": "buffer", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "render_pass_t::run", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L42", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "gles::run_in_context", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L44", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "gles::bind_render_buffer", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L45", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "glGenRenderbuffers", "is_member_call": false, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L46", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "glBindRenderbuffer", "is_member_call": false, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L46", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "glRenderbufferStorage", "is_member_call": false, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L47", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "glFramebufferRenderbuffer", "is_member_call": false, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L48", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "gen_render_instances", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L51", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "scene", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L51", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_core", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L51", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "framebuffer_region_from_geometry_region", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L54", "receiver": "target", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "render_pass_t::run", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L56", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "gles::run_in_context", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L57", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "gles::bind_render_buffer", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L58", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "glFramebufferRenderbuffer", "is_member_call": false, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L59", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "glDeleteRenderbuffers", "is_member_call": false, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L60", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "data", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L64", "receiver": "pixels", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "wlr_texture_read_pixels", "is_member_call": false, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L65", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_texture", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L65", "receiver": "buffer", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "pixman_region32_contains_point", "is_member_call": false, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L70", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "to_pixman", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L70", "receiver": "allowed", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "register_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L76", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "find_output", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L78", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_core", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L78", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_all_views", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L80", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_core", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L80", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_output", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L81", "receiver": "candidate", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "toplevel_cast", "is_member_call": false, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L81", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "toplevel_cast", "is_member_call": false, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L82", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_current_workspace", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L83", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "wset", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L83", "receiver": "output", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "emit", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L84", "receiver": "output", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "register_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L88", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "register_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L92", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "has_member", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L96", "receiver": "data", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "close", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L98", "receiver": "solid", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "reset", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L98", "receiver": "solid", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "find_output", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L101", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_core", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L101", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "color_rect_view_t::create", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L103", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "set_color", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L105", "receiver": "solid", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "set_geometry", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L106", "receiver": "solid", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "set_border", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L107", "receiver": "solid", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "register_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L110", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "front", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L112", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_outputs", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L112", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_core", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L112", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_layout_geometry", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L114", "receiver": "output", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "allocate", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L116", "receiver": "buffer", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "render_desktop", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L121", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "data", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L124", "receiver": "pixels", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "wlr_texture_read_pixels", "is_member_call": false, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L125", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_texture", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L125", "receiver": "buffer", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "getenv", "is_member_call": false, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L126", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "put", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L128", "receiver": "f", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "put", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L128", "receiver": "f", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "put", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L128", "receiver": "f", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "register_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L131", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "front", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L135", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_outputs", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L135", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_core", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L135", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "color_rect_view_t::create", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L137", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "set_color", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L138", "receiver": "rectangle", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "has_member", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L138", "receiver": "data", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "set_geometry", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L140", "receiver": "rectangle", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "has_member", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L140", "receiver": "data", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "damage", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L146", "receiver": "rectangle", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "scene::set_node_enabled", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L147", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_root_node", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L147", "receiver": "rectangle", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "close", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L148", "receiver": "rectangle", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "reset", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L148", "receiver": "rectangle", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "register_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L152", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "has_member", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L155", "receiver": "data", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "find_output", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L156", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_core", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L156", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "front", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L157", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_outputs", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L157", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_core", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L157", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "has_member", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L158", "receiver": "data", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "has_member", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L160", "receiver": "data", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "emit", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L161", "receiver": "output", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "is_plugin_active", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L162", "receiver": "output", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "register_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L166", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_outputs", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L168", "receiver": null, "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "get_core", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L168", "receiver": "wf", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_init", "callee": "set_inhibited", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L169", "receiver": "output", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "callee": "close", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L175", "receiver": "solid", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "callee": "unregister_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L176", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "callee": "unregister_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L177", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "callee": "unregister_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L178", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "callee": "unregister_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L179", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "callee": "close", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L180", "receiver": "rectangle", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "callee": "reset", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L180", "receiver": "rectangle", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "callee": "unregister_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L181", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "callee": "unregister_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L182", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "callee": "unregister_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L183", "receiver": "repo", "lang": "cpp"}, {"caller_nid": "$graphify-root$_tests_render_regression_render_test_render_test_t_fini", "callee": "unregister_method", "is_member_call": true, "source_file": "tests/render-regression/render-test.cpp", "source_location": "L184", "receiver": "repo", "lang": "cpp"}]}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/656d91283bfb50cf8c84cfcefe5833d9ff9d753ce2ca9343a63d3710335a95ef.json b/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/656d91283bfb50cf8c84cfcefe5833d9ff9d753ce2ca9343a63d3710335a95ef.json deleted file mode 100644 index 5c51a59fdb65..000000000000 --- a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/656d91283bfb50cf8c84cfcefe5833d9ff9d753ce2ca9343a63d3710335a95ef.json +++ /dev/null @@ -1 +0,0 @@ -{"nodes": [{"id": "$graphify-root$_files_wayfire_session_2", "label": "wayfire-session-2", "file_type": "code", "source_file": "files/wayfire-session-2", "source_location": "L1", "metadata": {"language": "bash", "kind": "file"}}, {"id": "$graphify-root$_files_wayfire_session_2__entry", "label": "wayfire-session-2 script", "file_type": "code", "source_file": "files/wayfire-session-2", "source_location": "L1", "metadata": {"language": "bash", "kind": "bash_entrypoint"}}, {"id": "$graphify-root$_files_wayfire_session_2_xdg_session_type", "label": "XDG_SESSION_TYPE", "file_type": "code", "source_file": "files/wayfire-session-2", "source_location": "L12", "metadata": {"language": "bash", "kind": "code"}}, {"id": "$graphify-root$_files_wayfire_session_2_gdk_backend", "label": "GDK_BACKEND", "file_type": "code", "source_file": "files/wayfire-session-2", "source_location": "L13", "metadata": {"language": "bash", "kind": "code"}}, {"id": "$graphify-root$_files_wayfire_session_2_qt_qpa_platform", "label": "QT_QPA_PLATFORM", "file_type": "code", "source_file": "files/wayfire-session-2", "source_location": "L14", "metadata": {"language": "bash", "kind": "code"}}, {"id": "$graphify-root$_files_wayfire_session_2_moz_enable_wayland", "label": "MOZ_ENABLE_WAYLAND", "file_type": "code", "source_file": "files/wayfire-session-2", "source_location": "L15", "metadata": {"language": "bash", "kind": "code"}}], "edges": [{"source": "$graphify-root$_files_wayfire_session_2", "target": "$graphify-root$_files_wayfire_session_2__entry", "relation": "contains", "confidence": "EXTRACTED", "source_file": "files/wayfire-session-2", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_session_2", "target": "$graphify-root$_files_wayfire_session_2_xdg_session_type", "relation": "defines", "confidence": "EXTRACTED", "source_file": "files/wayfire-session-2", "source_location": "L12", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_session_2", "target": "$graphify-root$_files_wayfire_session_2_gdk_backend", "relation": "defines", "confidence": "EXTRACTED", "source_file": "files/wayfire-session-2", "source_location": "L13", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_session_2", "target": "$graphify-root$_files_wayfire_session_2_qt_qpa_platform", "relation": "defines", "confidence": "EXTRACTED", "source_file": "files/wayfire-session-2", "source_location": "L14", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_session_2", "target": "$graphify-root$_files_wayfire_session_2_moz_enable_wayland", "relation": "defines", "confidence": "EXTRACTED", "source_file": "files/wayfire-session-2", "source_location": "L15", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_session_2", "target": "eprefix_etc_wayfire_env", "relation": "imports", "confidence": "EXTRACTED", "source_file": "files/wayfire-session-2", "source_location": "L24", "weight": 1.0, "context": "import"}], "raw_calls": [{"language": "bash", "callee": "exec", "caller_nid": "$graphify-root$_files_wayfire_session_2__entry", "source_file": "files/wayfire-session-2", "source_location": "L5"}, {"language": "bash", "callee": "cp", "caller_nid": "$graphify-root$_files_wayfire_session_2__entry", "source_file": "files/wayfire-session-2", "source_location": "L20"}], "bash_sources": []}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/7c7bd637f54d282130e0eb0462a0d558e96532feae972902a0159f8240148bb0.json b/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/7c7bd637f54d282130e0eb0462a0d558e96532feae972902a0159f8240148bb0.json deleted file mode 100644 index 0ae04525f193..000000000000 --- a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/7c7bd637f54d282130e0eb0462a0d558e96532feae972902a0159f8240148bb0.json +++ /dev/null @@ -1 +0,0 @@ -{"nodes": [{"id": "$graphify-root$_tests_render_regression_readme_md", "label": "README.md", "file_type": "document", "node_kind": "page", "source_file": "tests/render-regression/README.md", "source_location": "L1"}, {"id": "$graphify-root$_tests_render_regression_readme_cube_repaint_and_stacking_regressions", "label": "Cube repaint and stacking regressions", "file_type": "document", "node_kind": "heading", "source_file": "tests/render-regression/README.md", "source_location": "L1"}], "edges": [{"source": "$graphify-root$_tests_render_regression_readme_md", "target": "$graphify-root$_tests_render_regression_readme_cube_repaint_and_stacking_regressions", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/render-regression/README.md", "source_location": "L1", "weight": 1.0}], "input_tokens": 0, "output_tokens": 0}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/afd3dd0924bb0887d234a6470a4a582419a372ff02d515773236ee07980f3c86.json b/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/afd3dd0924bb0887d234a6470a4a582419a372ff02d515773236ee07980f3c86.json deleted file mode 100644 index 1338bf869431..000000000000 --- a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/afd3dd0924bb0887d234a6470a4a582419a372ff02d515773236ee07980f3c86.json +++ /dev/null @@ -1 +0,0 @@ -{"nodes": [{"id": "$graphify-root$_tests_render_regression_run_py", "label": "run.py", "file_type": "code", "source_file": "tests/render-regression/run.py", "source_location": "L1"}], "edges": [{"source": "$graphify-root$_tests_render_regression_run_py", "target": "argparse", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/run.py", "source_location": "L3", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_run_py", "target": "os", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/run.py", "source_location": "L4", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_run_py", "target": "pathlib", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/run.py", "source_location": "L5", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_run_py", "target": "shlex", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/run.py", "source_location": "L6", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_run_py", "target": "shutil", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/run.py", "source_location": "L7", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_run_py", "target": "subprocess", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/run.py", "source_location": "L8", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_run_py", "target": "tempfile", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/run.py", "source_location": "L9", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_run_py", "target": "time", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/run.py", "source_location": "L10", "weight": 1.0}], "raw_calls": []}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/b09805d473c41ea8845c8a1783d0cccc23e4c6a9e1cb3f1a8e5a7fdb0e559d25.json b/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/b09805d473c41ea8845c8a1783d0cccc23e4c6a9e1cb3f1a8e5a7fdb0e559d25.json deleted file mode 100644 index 9188104bb520..000000000000 --- a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/b09805d473c41ea8845c8a1783d0cccc23e4c6a9e1cb3f1a8e5a7fdb0e559d25.json +++ /dev/null @@ -1 +0,0 @@ -{"nodes": [{"id": "$graphify-root$_tests_render_regression_dynamic_check_py", "label": "dynamic-check.py", "file_type": "code", "source_file": "tests/render-regression/dynamic-check.py", "source_location": "L1"}], "edges": [{"source": "$graphify-root$_tests_render_regression_dynamic_check_py", "target": "time", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/dynamic-check.py", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_dynamic_check_py", "target": "io", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/dynamic-check.py", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_dynamic_check_py", "target": "subprocess", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/dynamic-check.py", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_dynamic_check_py", "target": "pil", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/dynamic-check.py", "source_location": "L2", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_dynamic_check_py", "target": "ipc", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/dynamic-check.py", "source_location": "L3", "weight": 1.0}], "raw_calls": []}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/c14d817ea53b36a9a95614c9a0f8afaab9b57168c25b4425d4fbbb7336c95a13.json b/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/c14d817ea53b36a9a95614c9a0f8afaab9b57168c25b4425d4fbbb7336c95a13.json deleted file mode 100644 index b760b5fbfd1a..000000000000 --- a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/c14d817ea53b36a9a95614c9a0f8afaab9b57168c25b4425d4fbbb7336c95a13.json +++ /dev/null @@ -1 +0,0 @@ -{"nodes": [{"id": "$graphify-root$_tests_render_regression_redraw_check_py", "label": "redraw-check.py", "file_type": "code", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L1"}, {"id": "$graphify-root$_tests_render_regression_redraw_check_check", "label": "check()", "file_type": "code", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L7", "_callable": true}, {"id": "$graphify-root$_tests_render_regression_redraw_check_shot", "label": "shot()", "file_type": "code", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L11", "_callable": true}, {"id": "$graphify-root$_tests_render_regression_redraw_check_solid", "label": "solid()", "file_type": "code", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L14", "_callable": true}], "edges": [{"source": "$graphify-root$_tests_render_regression_redraw_check_py", "target": "io", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_redraw_check_py", "target": "sys", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_redraw_check_py", "target": "time", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_redraw_check_py", "target": "subprocess", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_redraw_check_py", "target": "pathlib", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L2", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_redraw_check_py", "target": "pil", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L3", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_redraw_check_py", "target": "ipc", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L4", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_redraw_check_py", "target": "$graphify-root$_tests_render_regression_redraw_check_check", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L7", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_redraw_check_py", "target": "$graphify-root$_tests_render_regression_redraw_check_shot", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L11", "weight": 1.0}, {"source": "$graphify-root$_tests_render_regression_redraw_check_py", "target": "$graphify-root$_tests_render_regression_redraw_check_solid", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L14", "weight": 1.0}], "raw_calls": [{"caller_nid": "$graphify-root$_tests_render_regression_redraw_check_shot", "callee": "convert", "is_member_call": true, "source_file": "tests/render-regression/redraw-check.py", "source_location": "L12", "receiver": null}, {"caller_nid": "$graphify-root$_tests_render_regression_redraw_check_shot", "callee": "BytesIO", "is_member_call": true, "source_file": "tests/render-regression/redraw-check.py", "source_location": "L12", "receiver": "io"}, {"caller_nid": "$graphify-root$_tests_render_regression_redraw_check_shot", "callee": "check_output", "is_member_call": true, "source_file": "tests/render-regression/redraw-check.py", "source_location": "L12", "receiver": "subprocess"}, {"caller_nid": "$graphify-root$_tests_render_regression_redraw_check_shot", "callee": "ENV", "is_member_call": false, "indirect": true, "context": "argument", "source_file": "tests/render-regression/redraw-check.py", "source_location": "L12"}, {"caller_nid": "$graphify-root$_tests_render_regression_redraw_check_solid", "callee": "ipc", "is_member_call": false, "source_file": "tests/render-regression/redraw-check.py", "source_location": "L16", "receiver": null}]}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/e7aa91da4cf87f92e080fc64045b02641a5e02fe4f146744c6d14105840d38a3.json b/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/e7aa91da4cf87f92e080fc64045b02641a5e02fe4f146744c6d14105840d38a3.json deleted file mode 100644 index bc86bd7777f2..000000000000 --- a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/e7aa91da4cf87f92e080fc64045b02641a5e02fe4f146744c6d14105840d38a3.json +++ /dev/null @@ -1 +0,0 @@ -{"nodes": [{"id": "$graphify-root$_files_wayfire_session", "label": "wayfire-session", "file_type": "code", "source_file": "files/wayfire-session", "source_location": "L1", "metadata": {"language": "bash", "kind": "file"}}, {"id": "$graphify-root$_files_wayfire_session__entry", "label": "wayfire-session script", "file_type": "code", "source_file": "files/wayfire-session", "source_location": "L1", "metadata": {"language": "bash", "kind": "bash_entrypoint"}}, {"id": "$graphify-root$_files_wayfire_session_xdg_session_type", "label": "XDG_SESSION_TYPE", "file_type": "code", "source_file": "files/wayfire-session", "source_location": "L12", "metadata": {"language": "bash", "kind": "code"}}, {"id": "$graphify-root$_files_wayfire_session_gdk_backend", "label": "GDK_BACKEND", "file_type": "code", "source_file": "files/wayfire-session", "source_location": "L13", "metadata": {"language": "bash", "kind": "code"}}, {"id": "$graphify-root$_files_wayfire_session_qt_qpa_platform", "label": "QT_QPA_PLATFORM", "file_type": "code", "source_file": "files/wayfire-session", "source_location": "L14", "metadata": {"language": "bash", "kind": "code"}}, {"id": "$graphify-root$_files_wayfire_session_moz_enable_wayland", "label": "MOZ_ENABLE_WAYLAND", "file_type": "code", "source_file": "files/wayfire-session", "source_location": "L15", "metadata": {"language": "bash", "kind": "code"}}], "edges": [{"source": "$graphify-root$_files_wayfire_session", "target": "$graphify-root$_files_wayfire_session__entry", "relation": "contains", "confidence": "EXTRACTED", "source_file": "files/wayfire-session", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_session", "target": "$graphify-root$_files_wayfire_session_xdg_session_type", "relation": "defines", "confidence": "EXTRACTED", "source_file": "files/wayfire-session", "source_location": "L12", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_session", "target": "$graphify-root$_files_wayfire_session_gdk_backend", "relation": "defines", "confidence": "EXTRACTED", "source_file": "files/wayfire-session", "source_location": "L13", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_session", "target": "$graphify-root$_files_wayfire_session_qt_qpa_platform", "relation": "defines", "confidence": "EXTRACTED", "source_file": "files/wayfire-session", "source_location": "L14", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_session", "target": "$graphify-root$_files_wayfire_session_moz_enable_wayland", "relation": "defines", "confidence": "EXTRACTED", "source_file": "files/wayfire-session", "source_location": "L15", "weight": 1.0}], "raw_calls": [{"language": "bash", "callee": "exec", "caller_nid": "$graphify-root$_files_wayfire_session__entry", "source_file": "files/wayfire-session", "source_location": "L5"}], "bash_sources": []}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/f94d994c893c3549e0e32a67eed8bfde5b780f56890086c4daa8815d3dc0d4ad.json b/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/f94d994c893c3549e0e32a67eed8bfde5b780f56890086c4daa8815d3dc0d4ad.json deleted file mode 100644 index 56dc401bb137..000000000000 --- a/gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/f94d994c893c3549e0e32a67eed8bfde5b780f56890086c4daa8815d3dc0d4ad.json +++ /dev/null @@ -1 +0,0 @@ -{"nodes": [{"id": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_md", "label": "wayfire-0.10.1-transparent-cube-animation-notes.md", "file_type": "document", "node_kind": "page", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L1"}, {"id": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "label": "Transparent cube animation compatibility notes", "file_type": "document", "node_kind": "heading", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L1"}, {"id": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_goal", "label": "Goal", "file_type": "document", "node_kind": "heading", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L3"}, {"id": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_working_theory", "label": "Working theory", "file_type": "document", "node_kind": "heading", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L14"}, {"id": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_minimum_target", "label": "Minimum target", "file_type": "document", "node_kind": "heading", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L27"}, {"id": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_current_direction", "label": "Current Direction", "file_type": "document", "node_kind": "heading", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L55"}, {"id": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_avoid_for_now", "label": "Avoid for now", "file_type": "document", "node_kind": "heading", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L65"}], "edges": [{"source": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_md", "target": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "relation": "contains", "confidence": "EXTRACTED", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "target": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_goal", "relation": "contains", "confidence": "EXTRACTED", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L3", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "target": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_working_theory", "relation": "contains", "confidence": "EXTRACTED", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L14", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "target": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_minimum_target", "relation": "contains", "confidence": "EXTRACTED", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L27", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "target": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_current_direction", "relation": "contains", "confidence": "EXTRACTED", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L55", "weight": 1.0}, {"source": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "target": "$graphify-root$_files_wayfire_0_10_1_transparent_cube_animation_notes_avoid_for_now", "relation": "contains", "confidence": "EXTRACTED", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "source_location": "L65", "weight": 1.0}], "input_tokens": 0, "output_tokens": 0}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/cache/last_query_stamp b/gui-wm/wayfire/graphify-out/cache/last_query_stamp deleted file mode 100644 index 150057b31449..000000000000 --- a/gui-wm/wayfire/graphify-out/cache/last_query_stamp +++ /dev/null @@ -1 +0,0 @@ -1789784946.0656278
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/cache/stat-index.json b/gui-wm/wayfire/graphify-out/cache/stat-index.json deleted file mode 100644 index 19691f6c98bb..000000000000 --- a/gui-wm/wayfire/graphify-out/cache/stat-index.json +++ /dev/null @@ -1 +0,0 @@ -{"files/wayfire-0.10.1-transparent-cube-animation-notes.md":{"size":3204,"mtime_ns":1780715444271488977,"indexed_at_ns":1786919041011725353,"word_count":454,"hashes":{"files/wayfire-0.10.1-transparent-cube-animation-notes.md":"f94d994c893c3549e0e32a67eed8bfde5b780f56890086c4daa8815d3dc0d4ad"}},"files/wayfire-session":{"size":540,"mtime_ns":1653360147363039545,"indexed_at_ns":1786919041011320613,"word_count":63,"hashes":{"files/wayfire-session":"e7aa91da4cf87f92e080fc64045b02641a5e02fe4f146744c6d14105840d38a3"}},"files/wayfire-session-2":{"size":871,"mtime_ns":1780641764455803701,"indexed_at_ns":1786919041011593443,"word_count":98,"hashes":{"files/wayfire-session-2":"656d91283bfb50cf8c84cfcefe5833d9ff9d753ce2ca9343a63d3710335a95ef"}},"files/wayfire-0.11.0-cube-rendering-notes.md":{"size":1675,"mtime_ns":1789784668624786964,"indexed_at_ns":1789784690525126086,"word_count":235,"hashes":{"files/wayfire-0.11.0-cube-rendering-notes.md":"16b475fe1869b41ccaca5639bc87dfc15fe69c3ffe5edf9d9fd2b7e7b6985901"}},"tests/render-regression/README.md":{"size":1927,"mtime_ns":1789784668624786964,"indexed_at_ns":1789784690525259067,"word_count":268,"hashes":{"tests/render-regression/readme.md":"7c7bd637f54d282130e0eb0462a0d558e96532feae972902a0159f8240148bb0"}},"tests/render-regression/dynamic-check.py":{"size":895,"mtime_ns":1789784619219584829,"indexed_at_ns":1789784690524069645,"word_count":38,"hashes":{"tests/render-regression/dynamic-check.py":"b09805d473c41ea8845c8a1783d0cccc23e4c6a9e1cb3f1a8e5a7fdb0e559d25"}},"tests/render-regression/ipc.py":{"size":904,"mtime_ns":1789784619219584829,"indexed_at_ns":1789784690524257746,"word_count":80,"hashes":{"tests/render-regression/ipc.py":"05eafb6a3d31ffc3daa8668c07dd7a92234e8056dfbaf507b2d06c4c0ce29adc"}},"tests/render-regression/redraw-check.py":{"size":3094,"mtime_ns":1789784619219584829,"indexed_at_ns":1789784690524395456,"word_count":158,"hashes":{"tests/render-regression/redraw-check.py":"c14d817ea53b36a9a95614c9a0f8afaab9b57168c25b4425d4fbbb7336c95a13"}},"tests/render-regression/render-test.cpp":{"size":9460,"mtime_ns":1789784619219584829,"indexed_at_ns":1789784690524525376,"word_count":617,"hashes":{"tests/render-regression/render-test.cpp":"3043830d9822a41238110bbd2ec25f6ef2931793959d60403d4a33ce3aa5e684"}},"tests/render-regression/run.py":{"size":3136,"mtime_ns":1789784668624786964,"indexed_at_ns":1789784690524655216,"word_count":237,"hashes":{"tests/render-regression/run.py":"afd3dd0924bb0887d234a6470a4a582419a372ff02d515773236ee07980f3c86"}}}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/graph.html b/gui-wm/wayfire/graphify-out/graph.html deleted file mode 100644 index 19f1f0504d68..000000000000 --- a/gui-wm/wayfire/graphify-out/graph.html +++ /dev/null @@ -1,345 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> -<meta charset="UTF-8"> -<title>graphify - graphify-out/graph.html</title> -<script src="https://unpkg.com/vis-network@9.1.6/standalone/umd/vis-network.min.js" - integrity="sha384-Ux6phic9PEHJ38YtrijhkzyJ8yQlH8i/+buBR8s3mAZOJrP1gwyvAcIYl3GWtpX1" - crossorigin="anonymous"></script> -<style> - * { box-sizing: border-box; margin: 0; padding: 0; } - body { background: #0f0f1a; color: #e0e0e0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; display: flex; height: 100vh; overflow: hidden; } - #graph { flex: 1; } - #sidebar { width: 280px; background: #1a1a2e; border-left: 1px solid #2a2a4e; display: flex; flex-direction: column; overflow: hidden; } - #search-wrap { padding: 12px; border-bottom: 1px solid #2a2a4e; } - #search { width: 100%; background: #0f0f1a; border: 1px solid #3a3a5e; color: #e0e0e0; padding: 7px 10px; border-radius: 6px; font-size: 13px; outline: none; } - #search:focus { border-color: #4E79A7; } - #search-results { max-height: 140px; overflow-y: auto; padding: 4px 12px; border-bottom: 1px solid #2a2a4e; display: none; } - .search-item { padding: 4px 6px; cursor: pointer; border-radius: 4px; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } - .search-item:hover { background: #2a2a4e; } - #info-panel { padding: 14px; border-bottom: 1px solid #2a2a4e; min-height: 140px; } - #info-panel h3 { font-size: 13px; color: #aaa; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.05em; } - #info-content { font-size: 13px; color: #ccc; line-height: 1.6; } - #info-content .field { margin-bottom: 5px; } - #info-content .field b { color: #e0e0e0; } - #info-content .empty { color: #555; font-style: italic; } - .neighbor-link { display: block; padding: 2px 6px; margin: 2px 0; border-radius: 3px; cursor: pointer; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-left: 3px solid #333; } - .neighbor-link:hover { background: #2a2a4e; } - #neighbors-list { max-height: 160px; overflow-y: auto; margin-top: 4px; } - #legend-wrap { flex: 1; overflow-y: auto; padding: 12px; } - #legend-wrap h3 { font-size: 13px; color: #aaa; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.05em; } - .legend-item { display: flex; align-items: center; gap: 8px; padding: 4px 0; cursor: pointer; border-radius: 4px; font-size: 12px; } - .legend-item:hover { background: #2a2a4e; padding-left: 4px; } - .legend-item.dimmed { opacity: 0.35; } - .legend-dot { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; } - .legend-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - .legend-count { color: #666; font-size: 11px; } - #stats { padding: 10px 14px; border-top: 1px solid #2a2a4e; font-size: 11px; color: #555; } - #legend-controls { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; padding: 4px 0; } - #legend-controls label { display: flex; align-items: center; gap: 6px; cursor: pointer; font-size: 12px; color: #aaa; user-select: none; } - #legend-controls label:hover { color: #e0e0e0; } - .legend-cb, #select-all-cb { appearance: none; -webkit-appearance: none; width: 14px; height: 14px; border: 1.5px solid #3a3a5e; border-radius: 3px; background: #0f0f1a; cursor: pointer; position: relative; flex-shrink: 0; } - .legend-cb:checked, #select-all-cb:checked { background: #4E79A7; border-color: #4E79A7; } - .legend-cb:checked::after, #select-all-cb:checked::after { content: ''; position: absolute; left: 3.5px; top: 1px; width: 4px; height: 7px; border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg); } - #select-all-cb:indeterminate { background: #4E79A7; border-color: #4E79A7; } - #select-all-cb:indeterminate::after { content: ''; position: absolute; left: 2px; top: 5px; width: 8px; height: 2px; background: #fff; border: none; transform: none; } -</style> -</head> -<body> -<div id="graph"></div> -<div id="sidebar"> - <div id="search-wrap"> - <input id="search" type="text" placeholder="Search nodes..." autocomplete="off"> - <div id="search-results"></div> - </div> - <div id="info-panel"> - <h3>Node Info</h3> - <div id="info-content"><span class="empty">Click a node to inspect it</span></div> - </div> - <div id="legend-wrap"> - <h3>Communities</h3> - <div id="legend-controls"> - <label><input type="checkbox" id="select-all-cb" checked onchange="toggleAllCommunities(!this.checked)">Select All</label> - </div> - <div id="legend"></div> - </div> - <div id="stats">49 nodes · 46 edges · 10 communities</div> -</div> -<script> -const RAW_NODES = [{"id": "tests_render_regression_render_test_render_test_t", "label": "render_test_t", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 40.0, "font": {"size": 12, "color": "#ffffff"}, "title": "render_test_t", "community": 3, "community_name": "render_test_t", "source_file": "tests/render-regression/render-test.cpp", "file_type": "code", "degree": 17}, {"id": "tests_render_regression_render_test_render_test_t_fini", "label": ".fini()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": ".fini()", "community": 3, "community_name": "render_test_t", "source_file": "tests/render-regression/render-test.cpp", "file_type": "code", "degree": 1}, {"id": "tests_render_regression_ipc_ipc", "label": "ipc()", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 17.1, "font": {"size": 12, "color": "#ffffff"}, "title": "ipc()", "community": 4, "community_name": "redraw-check.py", "source_file": "tests/render-regression/ipc.py", "file_type": "code", "degree": 4}, {"id": "tests_render_regression_redraw_check_check", "label": "check()", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "check()", "community": 4, "community_name": "redraw-check.py", "source_file": "tests/render-regression/redraw-check.py", "file_type": "code", "degree": 1}, {"id": "tests_render_regression_redraw_check_shot", "label": "shot()", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "shot()", "community": 4, "community_name": "redraw-check.py", "source_file": "tests/render-regression/redraw-check.py", "file_type": "code", "degree": 1}, {"id": "tests_render_regression_redraw_check_solid", "label": "solid()", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 13.5, "font": {"size": 0, "color": "#ffffff"}, "title": "solid()", "community": 4, "community_name": "redraw-check.py", "source_file": "tests/render-regression/redraw-check.py", "file_type": "code", "degree": 2}, {"id": "tests_render_regression_render_test_render_test_t_init", "label": ".init()", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 13.5, "font": {"size": 0, "color": "#ffffff"}, "title": ".init()", "community": 7, "community_name": "string", "source_file": "tests/render-regression/render-test.cpp", "file_type": "code", "degree": 2}, {"id": "files_wayfire_0_10_1_transparent_cube_animation_notes", "label": "wayfire-0.10.1-transparent-cube-animation-notes.md", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "wayfire-0.10.1-transparent-cube-animation-notes.md", "community": 0, "community_name": "Transparent cube animation compatibility notes", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "file_type": "document", "degree": 1}, {"id": "files_wayfire_0_10_1_transparent_cube_animation_notes_avoid_for_now", "label": "Avoid for now", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "Avoid for now", "community": 0, "community_name": "Transparent cube animation compatibility notes", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "file_type": "document", "degree": 1}, {"id": "files_wayfire_0_10_1_transparent_cube_animation_notes_current_direction", "label": "Current Direction", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "Current Direction", "community": 0, "community_name": "Transparent cube animation compatibility notes", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "file_type": "document", "degree": 1}, {"id": "files_wayfire_0_10_1_transparent_cube_animation_notes_goal", "label": "Goal", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "Goal", "community": 0, "community_name": "Transparent cube animation compatibility notes", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "file_type": "document", "degree": 1}, {"id": "files_wayfire_0_10_1_transparent_cube_animation_notes_minimum_target", "label": "Minimum target", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "Minimum target", "community": 0, "community_name": "Transparent cube animation compatibility notes", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "file_type": "document", "degree": 1}, {"id": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "label": "Transparent cube animation compatibility notes", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 20.6, "font": {"size": 12, "color": "#ffffff"}, "title": "Transparent cube animation compatibility notes", "community": 0, "community_name": "Transparent cube animation compatibility notes", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "file_type": "document", "degree": 6}, {"id": "files_wayfire_0_10_1_transparent_cube_animation_notes_working_theory", "label": "Working theory", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "Working theory", "community": 0, "community_name": "Transparent cube animation compatibility notes", "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", "file_type": "document", "degree": 1}, {"id": "files_wayfire_session", "label": "wayfire-session", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 18.8, "font": {"size": 12, "color": "#ffffff"}, "title": "wayfire-session", "community": 1, "community_name": "wayfire-session", "source_file": "files/wayfire-session", "file_type": "code", "degree": 5}, {"id": "files_wayfire_session__entry", "label": "wayfire-session script", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "wayfire-session script", "community": 1, "community_name": "wayfire-session", "source_file": "files/wayfire-session", "file_type": "code", "degree": 1}, {"id": "files_wayfire_session_gdk_backend", "label": "GDK_BACKEND", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "GDK_BACKEND", "community": 1, "community_name": "wayfire-session", "source_file": "files/wayfire-session", "file_type": "code", "degree": 1}, {"id": "files_wayfire_session_moz_enable_wayland", "label": "MOZ_ENABLE_WAYLAND", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "MOZ_ENABLE_WAYLAND", "community": 1, "community_name": "wayfire-session", "source_file": "files/wayfire-session", "file_type": "code", "degree": 1}, {"id": "files_wayfire_session_qt_qpa_platform", "label": "QT_QPA_PLATFORM", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "QT_QPA_PLATFORM", "community": 1, "community_name": "wayfire-session", "source_file": "files/wayfire-session", "file_type": "code", "degree": 1}, {"id": "files_wayfire_session_xdg_session_type", "label": "XDG_SESSION_TYPE", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "XDG_SESSION_TYPE", "community": 1, "community_name": "wayfire-session", "source_file": "files/wayfire-session", "file_type": "code", "degree": 1}, {"id": "files_wayfire_session_2", "label": "wayfire-session-2", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 18.8, "font": {"size": 12, "color": "#ffffff"}, "title": "wayfire-session-2", "community": 2, "community_name": "wayfire-session-2", "source_file": "files/wayfire-session-2", "file_type": "code", "degree": 5}, {"id": "files_wayfire_session_2__entry", "label": "wayfire-session-2 script", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "wayfire-session-2 script", "community": 2, "community_name": "wayfire-session-2", "source_file": "files/wayfire-session-2", "file_type": "code", "degree": 1}, {"id": "files_wayfire_session_2_gdk_backend", "label": "GDK_BACKEND", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "GDK_BACKEND", "community": 2, "community_name": "wayfire-session-2", "source_file": "files/wayfire-session-2", "file_type": "code", "degree": 1}, {"id": "files_wayfire_session_2_moz_enable_wayland", "label": "MOZ_ENABLE_WAYLAND", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "MOZ_ENABLE_WAYLAND", "community": 2, "community_name": "wayfire-session-2", "source_file": "files/wayfire-session-2", "file_type": "code", "degree": 1}, {"id": "files_wayfire_session_2_qt_qpa_platform", "label": "QT_QPA_PLATFORM", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "QT_QPA_PLATFORM", "community": 2, "community_name": "wayfire-session-2", "source_file": "files/wayfire-session-2", "file_type": "code", "degree": 1}, {"id": "files_wayfire_session_2_xdg_session_type", "label": "XDG_SESSION_TYPE", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "XDG_SESSION_TYPE", "community": 2, "community_name": "wayfire-session-2", "source_file": "files/wayfire-session-2", "file_type": "code", "degree": 1}, {"id": "color_rect_view_t", "label": "color_rect_view_t", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "color_rect_view_t", "community": 3, "community_name": "render_test_t", "source_file": "", "file_type": "code", "degree": 1}, {"id": "connection_t", "label": "connection_t", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "connection_t", "community": 3, "community_name": "render_test_t", "source_file": "", "file_type": "code", "degree": 1}, {"id": "method_repository_t", "label": "method_repository_t", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "method_repository_t", "community": 3, "community_name": "render_test_t", "source_file": "", "file_type": "code", "degree": 1}, {"id": "plugin_interface_t", "label": "plugin_interface_t", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "plugin_interface_t", "community": 3, "community_name": "render_test_t", "source_file": "", "file_type": "code", "degree": 1}, {"id": "ref_ptr_t", "label": "ref_ptr_t", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "ref_ptr_t", "community": 3, "community_name": "render_test_t", "source_file": "", "file_type": "code", "degree": 1}, {"id": "render_pass_begin_signal", "label": "render_pass_begin_signal", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "render_pass_begin_signal", "community": 3, "community_name": "render_test_t", "source_file": "", "file_type": "code", "degree": 1}, {"id": "shared_ptr", "label": "shared_ptr", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "shared_ptr", "community": 3, "community_name": "render_test_t", "source_file": "", "file_type": "code", "degree": 1}, {"id": "tests_render_regression_render_test_render_test_t_count_passes", "label": "count_passes", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "count_passes", "community": 3, "community_name": "render_test_t", "source_file": "tests/render-regression/render-test.cpp", "file_type": "code", "degree": 1}, {"id": "tests_render_regression_render_test_render_test_t_passes", "label": "passes", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "passes", "community": 3, "community_name": "render_test_t", "source_file": "tests/render-regression/render-test.cpp", "file_type": "code", "degree": 1}, {"id": "tests_render_regression_render_test_render_test_t_rectangle", "label": "rectangle", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "rectangle", "community": 3, "community_name": "render_test_t", "source_file": "tests/render-regression/render-test.cpp", "file_type": "code", "degree": 1}, {"id": "tests_render_regression_render_test_render_test_t_repo", "label": "repo", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "repo", "community": 3, "community_name": "render_test_t", "source_file": "tests/render-regression/render-test.cpp", "file_type": "code", "degree": 1}, {"id": "tests_render_regression_render_test_render_test_t_solids", "label": "solids", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "solids", "community": 3, "community_name": "render_test_t", "source_file": "tests/render-regression/render-test.cpp", "file_type": "code", "degree": 1}, {"id": "tests_render_regression_dynamic_check", "label": "dynamic-check.py", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 13.5, "font": {"size": 0, "color": "#ffffff"}, "title": "dynamic-check.py", "community": 4, "community_name": "redraw-check.py", "source_file": "tests/render-regression/dynamic-check.py", "file_type": "code", "degree": 2}, {"id": "tests_render_regression_ipc", "label": "ipc.py", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 15.3, "font": {"size": 12, "color": "#ffffff"}, "title": "ipc.py", "community": 4, "community_name": "redraw-check.py", "source_file": "tests/render-regression/ipc.py", "file_type": "code", "degree": 3}, {"id": "tests_render_regression_redraw_check", "label": "redraw-check.py", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 18.8, "font": {"size": 12, "color": "#ffffff"}, "title": "redraw-check.py", "community": 4, "community_name": "redraw-check.py", "source_file": "tests/render-regression/redraw-check.py", "file_type": "code", "degree": 5}, {"id": "files_wayfire_0_11_0_cube_rendering_notes", "label": "wayfire-0.11.0-cube-rendering-notes.md", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "wayfire-0.11.0-cube-rendering-notes.md", "community": 5, "community_name": "wayfire-0.11.0-cube-rendering-notes.md", "source_file": "files/wayfire-0.11.0-cube-rendering-notes.md", "file_type": "document", "degree": 1}, {"id": "files_wayfire_0_11_0_cube_rendering_notes_cube_damage_and_layer_ordering_revision_3", "label": "Cube damage and layer ordering, revision 3", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "Cube damage and layer ordering, revision 3", "community": 5, "community_name": "wayfire-0.11.0-cube-rendering-notes.md", "source_file": "files/wayfire-0.11.0-cube-rendering-notes.md", "file_type": "document", "degree": 1}, {"id": "map", "label": "map", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 13.5, "font": {"size": 0, "color": "#ffffff"}, "title": "map", "community": 6, "community_name": "map", "source_file": "", "file_type": "code", "degree": 2}, {"id": "tests_render_regression_render_test", "label": "render-test.cpp", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 13.5, "font": {"size": 0, "color": "#ffffff"}, "title": "render-test.cpp", "community": 6, "community_name": "map", "source_file": "tests/render-regression/render-test.cpp", "file_type": "code", "degree": 2}, {"id": "string", "label": "string", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 13.5, "font": {"size": 0, "color": "#ffffff"}, "title": "string", "community": 7, "community_name": "string", "source_file": "", "file_type": "code", "degree": 2}, {"id": "tests_render_regression_readme", "label": "README.md", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "README.md", "community": 8, "community_name": "README.md", "source_file": "tests/render-regression/README.md", "file_type": "document", "degree": 1}, {"id": "tests_render_regression_readme_cube_repaint_and_stacking_regressions", "label": "Cube repaint and stacking regressions", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 11.8, "font": {"size": 0, "color": "#ffffff"}, "title": "Cube repaint and stacking regressions", "community": 8, "community_name": "README.md", "source_file": "tests/render-regression/README.md", "file_type": "document", "degree": 1}, {"id": "tests_render_regression_run", "label": "run.py", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "run.py", "community": 9, "community_name": "run.py", "source_file": "tests/render-regression/run.py", "file_type": "code", "degree": 0}]; -const RAW_EDGES = [{"from": "tests_render_regression_render_test_render_test_t", "to": "tests_render_regression_render_test_render_test_t_repo", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "tests_render_regression_render_test_render_test_t_rectangle", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "tests_render_regression_render_test_render_test_t_solids", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "tests_render_regression_render_test_render_test_t_passes", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "tests_render_regression_render_test_render_test_t_count_passes", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "ref_ptr_t", "label": "references", "title": "references [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "map", "label": "references", "title": "references [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "connection_t", "label": "references", "title": "references [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "method_repository_t", "label": "references", "title": "references [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "color_rect_view_t", "label": "references", "title": "references [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "shared_ptr", "label": "references", "title": "references [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "string", "label": "references", "title": "references [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "render_pass_begin_signal", "label": "references", "title": "references [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "tests_render_regression_render_test", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "plugin_interface_t", "label": "inherits", "title": "inherits [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "tests_render_regression_render_test_render_test_t_fini", "label": "method", "title": "method [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t", "to": "tests_render_regression_render_test_render_test_t_init", "label": "method", "title": "method [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_ipc_ipc", "to": "tests_render_regression_redraw_check_solid", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_ipc_ipc", "to": "tests_render_regression_dynamic_check", "label": "imports", "title": "imports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_ipc_ipc", "to": "tests_render_regression_redraw_check", "label": "imports", "title": "imports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_ipc_ipc", "to": "tests_render_regression_ipc", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_redraw_check_check", "to": "tests_render_regression_redraw_check", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_redraw_check_shot", "to": "tests_render_regression_redraw_check", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_redraw_check_solid", "to": "tests_render_regression_redraw_check", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_render_test_render_test_t_init", "to": "string", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_0_10_1_transparent_cube_animation_notes", "to": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_0_10_1_transparent_cube_animation_notes_avoid_for_now", "to": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_0_10_1_transparent_cube_animation_notes_current_direction", "to": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_0_10_1_transparent_cube_animation_notes_goal", "to": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_0_10_1_transparent_cube_animation_notes_minimum_target", "to": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", "to": "files_wayfire_0_10_1_transparent_cube_animation_notes_working_theory", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_session", "to": "files_wayfire_session__entry", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_session", "to": "files_wayfire_session_xdg_session_type", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_session", "to": "files_wayfire_session_gdk_backend", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_session", "to": "files_wayfire_session_qt_qpa_platform", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_session", "to": "files_wayfire_session_moz_enable_wayland", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_session_2", "to": "files_wayfire_session_2__entry", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_session_2", "to": "files_wayfire_session_2_xdg_session_type", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_session_2", "to": "files_wayfire_session_2_gdk_backend", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_session_2", "to": "files_wayfire_session_2_qt_qpa_platform", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_session_2", "to": "files_wayfire_session_2_moz_enable_wayland", "label": "defines", "title": "defines [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_dynamic_check", "to": "tests_render_regression_ipc", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_ipc", "to": "tests_render_regression_redraw_check", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "files_wayfire_0_11_0_cube_rendering_notes", "to": "files_wayfire_0_11_0_cube_rendering_notes_cube_damage_and_layer_ordering_revision_3", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "map", "to": "tests_render_regression_render_test", "label": "imports", "title": "imports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tests_render_regression_readme", "to": "tests_render_regression_readme_cube_repaint_and_stacking_regressions", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}]; -const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Transparent cube animation compatibility notes", "count": 7}, {"cid": 1, "color": "#F28E2B", "label": "wayfire-session", "count": 6}, {"cid": 2, "color": "#E15759", "label": "wayfire-session-2", "count": 6}, {"cid": 3, "color": "#76B7B2", "label": "render_test_t", "count": 14}, {"cid": 4, "color": "#59A14F", "label": "redraw-check.py", "count": 7}, {"cid": 5, "color": "#EDC948", "label": "wayfire-0.11.0-cube-rendering-notes.md", "count": 2}, {"cid": 6, "color": "#B07AA1", "label": "map", "count": 2}, {"cid": 7, "color": "#FF9DA7", "label": "string", "count": 2}, {"cid": 8, "color": "#9C755F", "label": "README.md", "count": 2}, {"cid": 9, "color": "#BAB0AC", "label": "run.py", "count": 1}]; - -// HTML-escape helper — prevents XSS when injecting graph data into innerHTML -function esc(s) { - return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,'''); -} - -// Build vis datasets -const nodesDS = new vis.DataSet(RAW_NODES.map(n => ({ - id: n.id, label: n.label, color: n.color, size: n.size, - font: n.font, title: n.title, - _community: n.community, _community_name: n.community_name, - _source_file: n.source_file, _file_type: n.file_type, _degree: n.degree, -}))); - -const edgesDS = new vis.DataSet(RAW_EDGES.map((e, i) => ({ - id: i, from: e.from, to: e.to, - label: '', - title: e.title, - dashes: e.dashes, - width: e.width, - color: e.color, - arrows: { to: { enabled: true, scaleFactor: 0.5 } }, -}))); - -const container = document.getElementById('graph'); -const network = new vis.Network(container, { nodes: nodesDS, edges: edgesDS }, { - physics: { - enabled: true, - solver: 'forceAtlas2Based', - forceAtlas2Based: { - gravitationalConstant: -60, - centralGravity: 0.005, - springLength: 120, - springConstant: 0.08, - damping: 0.4, - avoidOverlap: 0.8, - }, - stabilization: { iterations: 200, fit: true }, - }, - interaction: { - hover: true, - tooltipDelay: 100, - hideEdgesOnDrag: true, - navigationButtons: false, - keyboard: false, - }, - nodes: { shape: 'dot', borderWidth: 1.5 }, - edges: { smooth: { type: 'continuous', roundness: 0.2 }, selectionWidth: 3 }, -}); - -network.once('stabilizationIterationsDone', () => { - network.setOptions({ physics: { enabled: false } }); -}); - -function showInfo(nodeId) { - const n = nodesDS.get(nodeId); - if (!n) return; - const neighborIds = network.getConnectedNodes(nodeId); - const neighborItems = neighborIds.map(nid => { - const nb = nodesDS.get(nid); - const color = nb ? nb.color.background : '#555'; - return `<span class="neighbor-link" style="border-left-color:${esc(color)}" data-nid="${esc(nid)}">${esc(nb ? nb.label : nid)}</span>`; - }).join(''); - document.getElementById('info-content').innerHTML = ` - <div class="field"><b>${esc(n.label)}</b></div> - <div class="field">Type: ${esc(n._file_type || 'unknown')}</div> - <div class="field">Community: ${esc(n._community_name)}</div> - <div class="field">Source: ${esc(n._source_file || '-')}</div> - <div class="field">Degree: ${n._degree}</div> - ${neighborIds.length ? `<div class="field" style="margin-top:8px;color:#aaa;font-size:11px">Neighbors (${neighborIds.length})</div><div id="neighbors-list">${neighborItems}</div>` : ''} - `; -} - -function focusNode(nodeId) { - network.focus(nodeId, { scale: 1.4, animation: true }); - network.selectNodes([nodeId]); - showInfo(nodeId); -} - -// Neighbor links use a data attribute + one delegated listener rather than an -// inline onclick. A node id/label sourced from a document or a scraped URL -// (graphify add) can contain a double-quote; dropping the stringified id -// unescaped into a quoted onclick both broke every link and allowed a hostile -// source to inject an event handler into the local report (stored XSS, #1838). -// esc() on data-nid keeps the value inside the attribute; the listener reads it -// back verbatim. Bound to document so it survives the innerHTML rebuild that -// recreates #neighbors-list on each showInfo(). -document.addEventListener('click', e => { - const el = e.target.closest('.neighbor-link'); - if (el && el.dataset.nid !== undefined) focusNode(el.dataset.nid); -}); - -// Track hovered node — hover detection is more reliable than click params -let hoveredNodeId = null; -network.on('hoverNode', params => { - hoveredNodeId = params.node; - container.style.cursor = 'pointer'; -}); -network.on('blurNode', () => { - hoveredNodeId = null; - container.style.cursor = 'default'; -}); -container.addEventListener('click', () => { - if (hoveredNodeId !== null) { - showInfo(hoveredNodeId); - network.selectNodes([hoveredNodeId]); - } -}); -network.on('click', params => { - if (params.nodes.length > 0) { - showInfo(params.nodes[0]); - } else if (hoveredNodeId === null) { - document.getElementById('info-content').innerHTML = '<span class="empty">Click a node to inspect it</span>'; - } -}); - -const searchInput = document.getElementById('search'); -const searchResults = document.getElementById('search-results'); -searchInput.addEventListener('input', () => { - const q = searchInput.value.toLowerCase().trim(); - searchResults.innerHTML = ''; - if (!q) { searchResults.style.display = 'none'; return; } - const matches = RAW_NODES.filter(n => n.label.toLowerCase().includes(q)).slice(0, 20); - if (!matches.length) { searchResults.style.display = 'none'; return; } - searchResults.style.display = 'block'; - matches.forEach(n => { - const el = document.createElement('div'); - el.className = 'search-item'; - el.textContent = n.label; - el.style.borderLeft = `3px solid ${n.color.background}`; - el.style.paddingLeft = '8px'; - el.onclick = () => { - network.focus(n.id, { scale: 1.5, animation: true }); - network.selectNodes([n.id]); - showInfo(n.id); - searchResults.style.display = 'none'; - searchInput.value = ''; - }; - searchResults.appendChild(el); - }); -}); -document.addEventListener('click', e => { - if (!searchResults.contains(e.target) && e.target !== searchInput) - searchResults.style.display = 'none'; -}); - -const hiddenCommunities = new Set(); - -const selectAllCb = document.getElementById('select-all-cb'); - -function updateSelectAllState() { - const total = LEGEND.length; - const hidden = hiddenCommunities.size; - selectAllCb.checked = hidden === 0; - selectAllCb.indeterminate = hidden > 0 && hidden < total; -} - -function toggleAllCommunities(hide) { - document.querySelectorAll('.legend-item').forEach(item => { - hide ? item.classList.add('dimmed') : item.classList.remove('dimmed'); - }); - document.querySelectorAll('.legend-cb').forEach(cb => { - cb.checked = !hide; - }); - LEGEND.forEach(c => { - if (hide) hiddenCommunities.add(c.cid); else hiddenCommunities.delete(c.cid); - }); - const updates = RAW_NODES.map(n => ({ id: n.id, hidden: hide })); - nodesDS.update(updates); - updateSelectAllState(); -} - -const legendEl = document.getElementById('legend'); -LEGEND.forEach(c => { - const item = document.createElement('div'); - item.className = 'legend-item'; - const cb = document.createElement('input'); - cb.type = 'checkbox'; - cb.className = 'legend-cb'; - cb.checked = true; - cb.addEventListener('change', (e) => { - e.stopPropagation(); - if (cb.checked) { - hiddenCommunities.delete(c.cid); - item.classList.remove('dimmed'); - } else { - hiddenCommunities.add(c.cid); - item.classList.add('dimmed'); - } - const updates = RAW_NODES - .filter(n => n.community === c.cid) - .map(n => ({ id: n.id, hidden: !cb.checked })); - nodesDS.update(updates); - updateSelectAllState(); - }); - item.innerHTML = `<div class="legend-dot" style="background:${c.color}"></div> - <span class="legend-label">${c.label}</span> - <span class="legend-count">${c.count}</span>`; - item.prepend(cb); - item.onclick = (e) => { - if (e.target === cb) return; - cb.checked = !cb.checked; - cb.dispatchEvent(new Event('change')); - }; - legendEl.appendChild(item); -}); -</script> -<script> -// Render hyperedges as shaded regions -const hyperedges = []; -// afterDrawing passes ctx already transformed to network coordinate space. -// Draw node positions raw — no manual pan/zoom/DPR math needed. - -// Andrew's monotone chain. Returns the hull in counter-clockwise order, which -// is what the perimeter must be traced in. Collinear and duplicate points -// collapse to the extremes, so degenerate member sets render as a segment -// rather than a zero-area crossed path. -function convexHull(pts) { - const p = pts.slice().sort((a, b) => (a.x - b.x) || (a.y - b.y)); - if (p.length < 3) return p; - const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x); - const build = seq => { - const out = []; - for (const q of seq) { - while (out.length >= 2 && cross(out[out.length - 2], out[out.length - 1], q) <= 0) out.pop(); - out.push(q); - } - out.pop(); - return out; - }; - const hull = build(p).concat(build(p.slice().reverse())); - return hull.length >= 3 ? hull : p; -} -network.on('afterDrawing', function(ctx) { - hyperedges.forEach(h => { - const positions = h.nodes - .map(nid => network.getPositions([nid])[nid]) - .filter(p => p !== undefined); - if (positions.length < 2) return; - ctx.save(); - ctx.globalAlpha = 0.12; - ctx.fillStyle = '#6366f1'; - ctx.strokeStyle = '#6366f1'; - ctx.lineWidth = 2; - ctx.beginPath(); - // Centroid and expanded hull in network coordinates. - // The perimeter must follow hull order, not h.nodes order: tracing the - // raw member order self-intersects whenever the layout does not happen - // to place members in angular order, filling as crossed wedges. - const cx = positions.reduce((s, p) => s + p.x, 0) / positions.length; - const cy = positions.reduce((s, p) => s + p.y, 0) / positions.length; - const hull = convexHull(positions); - const expanded = hull.map(p => ({ - x: cx + (p.x - cx) * 1.15, - y: cy + (p.y - cy) * 1.15 - })); - ctx.moveTo(expanded[0].x, expanded[0].y); - expanded.slice(1).forEach(p => ctx.lineTo(p.x, p.y)); - ctx.closePath(); - ctx.fill(); - ctx.globalAlpha = 0.4; - ctx.stroke(); - // Label - ctx.globalAlpha = 0.8; - ctx.fillStyle = '#4f46e5'; - ctx.font = 'bold 11px sans-serif'; - ctx.textAlign = 'center'; - ctx.fillText(h.label, cx, cy - 5); - ctx.restore(); - }); -}); -</script> -</body> -</html>
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/graph.json b/gui-wm/wayfire/graphify-out/graph.json deleted file mode 100644 index 6f60b65920d6..000000000000 --- a/gui-wm/wayfire/graphify-out/graph.json +++ /dev/null @@ -1,1142 +0,0 @@ -{ - "directed": false, - "multigraph": false, - "graph": {}, - "nodes": [ - { - "id": "tests_render_regression_render_test_render_test_t", - "label": "render_test_t", - "_callable": true, - "_callable_class": true, - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "render_test_t", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L19" - }, - { - "id": "tests_render_regression_render_test_render_test_t_fini", - "label": ".fini()", - "_callable": true, - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": ".fini()", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L173" - }, - { - "id": "tests_render_regression_ipc_ipc", - "label": "ipc()", - "_callable": true, - "_origin": "ast", - "community": 4, - "community_name": "redraw-check.py", - "file_type": "code", - "norm_label": "ipc()", - "source_file": "tests/render-regression/ipc.py", - "source_location": "L9" - }, - { - "id": "tests_render_regression_redraw_check_check", - "label": "check()", - "_callable": true, - "_origin": "ast", - "community": 4, - "community_name": "redraw-check.py", - "file_type": "code", - "norm_label": "check()", - "source_file": "tests/render-regression/redraw-check.py", - "source_location": "L7" - }, - { - "id": "tests_render_regression_redraw_check_shot", - "label": "shot()", - "_callable": true, - "_origin": "ast", - "community": 4, - "community_name": "redraw-check.py", - "file_type": "code", - "norm_label": "shot()", - "source_file": "tests/render-regression/redraw-check.py", - "source_location": "L11" - }, - { - "id": "tests_render_regression_redraw_check_solid", - "label": "solid()", - "_callable": true, - "_origin": "ast", - "community": 4, - "community_name": "redraw-check.py", - "file_type": "code", - "norm_label": "solid()", - "source_file": "tests/render-regression/redraw-check.py", - "source_location": "L14" - }, - { - "id": "tests_render_regression_render_test_render_test_t_init", - "label": ".init()", - "_callable": true, - "_origin": "ast", - "community": 7, - "community_name": "string", - "file_type": "code", - "norm_label": ".init()", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L28" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes", - "label": "wayfire-0.10.1-transparent-cube-animation-notes.md", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "page", - "norm_label": "wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L1" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes_avoid_for_now", - "label": "Avoid for now", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "heading", - "norm_label": "avoid for now", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L65" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes_current_direction", - "label": "Current Direction", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "heading", - "norm_label": "current direction", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L55" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes_goal", - "label": "Goal", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "heading", - "norm_label": "goal", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L3" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes_minimum_target", - "label": "Minimum target", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "heading", - "norm_label": "minimum target", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L27" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "label": "Transparent cube animation compatibility notes", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "heading", - "norm_label": "transparent cube animation compatibility notes", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L1" - }, - { - "id": "files_wayfire_0_10_1_transparent_cube_animation_notes_working_theory", - "label": "Working theory", - "_origin": "ast", - "community": 0, - "community_name": "Transparent cube animation compatibility notes", - "file_type": "document", - "node_kind": "heading", - "norm_label": "working theory", - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L14" - }, - { - "id": "files_wayfire_session", - "label": "wayfire-session", - "_origin": "ast", - "community": 1, - "community_name": "wayfire-session", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "file" - }, - "norm_label": "wayfire-session", - "source_file": "files/wayfire-session", - "source_location": "L1" - }, - { - "id": "files_wayfire_session__entry", - "label": "wayfire-session script", - "_origin": "ast", - "community": 1, - "community_name": "wayfire-session", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "bash_entrypoint" - }, - "norm_label": "wayfire-session script", - "source_file": "files/wayfire-session", - "source_location": "L1" - }, - { - "id": "files_wayfire_session_gdk_backend", - "label": "GDK_BACKEND", - "_origin": "ast", - "community": 1, - "community_name": "wayfire-session", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "gdk_backend", - "source_file": "files/wayfire-session", - "source_location": "L13" - }, - { - "id": "files_wayfire_session_moz_enable_wayland", - "label": "MOZ_ENABLE_WAYLAND", - "_origin": "ast", - "community": 1, - "community_name": "wayfire-session", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "moz_enable_wayland", - "source_file": "files/wayfire-session", - "source_location": "L15" - }, - { - "id": "files_wayfire_session_qt_qpa_platform", - "label": "QT_QPA_PLATFORM", - "_origin": "ast", - "community": 1, - "community_name": "wayfire-session", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "qt_qpa_platform", - "source_file": "files/wayfire-session", - "source_location": "L14" - }, - { - "id": "files_wayfire_session_xdg_session_type", - "label": "XDG_SESSION_TYPE", - "_origin": "ast", - "community": 1, - "community_name": "wayfire-session", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "xdg_session_type", - "source_file": "files/wayfire-session", - "source_location": "L12" - }, - { - "id": "files_wayfire_session_2", - "label": "wayfire-session-2", - "_origin": "ast", - "community": 2, - "community_name": "wayfire-session-2", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "file" - }, - "norm_label": "wayfire-session-2", - "source_file": "files/wayfire-session-2", - "source_location": "L1" - }, - { - "id": "files_wayfire_session_2__entry", - "label": "wayfire-session-2 script", - "_origin": "ast", - "community": 2, - "community_name": "wayfire-session-2", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "bash_entrypoint" - }, - "norm_label": "wayfire-session-2 script", - "source_file": "files/wayfire-session-2", - "source_location": "L1" - }, - { - "id": "files_wayfire_session_2_gdk_backend", - "label": "GDK_BACKEND", - "_origin": "ast", - "community": 2, - "community_name": "wayfire-session-2", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "gdk_backend", - "source_file": "files/wayfire-session-2", - "source_location": "L13" - }, - { - "id": "files_wayfire_session_2_moz_enable_wayland", - "label": "MOZ_ENABLE_WAYLAND", - "_origin": "ast", - "community": 2, - "community_name": "wayfire-session-2", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "moz_enable_wayland", - "source_file": "files/wayfire-session-2", - "source_location": "L15" - }, - { - "id": "files_wayfire_session_2_qt_qpa_platform", - "label": "QT_QPA_PLATFORM", - "_origin": "ast", - "community": 2, - "community_name": "wayfire-session-2", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "qt_qpa_platform", - "source_file": "files/wayfire-session-2", - "source_location": "L14" - }, - { - "id": "files_wayfire_session_2_xdg_session_type", - "label": "XDG_SESSION_TYPE", - "_origin": "ast", - "community": 2, - "community_name": "wayfire-session-2", - "file_type": "code", - "metadata": { - "language": "bash", - "kind": "code" - }, - "norm_label": "xdg_session_type", - "source_file": "files/wayfire-session-2", - "source_location": "L12" - }, - { - "id": "color_rect_view_t", - "label": "color_rect_view_t", - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "color_rect_view_t", - "source_file": "", - "source_location": "" - }, - { - "id": "connection_t", - "label": "connection_t", - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "connection_t", - "source_file": "", - "source_location": "" - }, - { - "id": "method_repository_t", - "label": "method_repository_t", - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "method_repository_t", - "source_file": "", - "source_location": "" - }, - { - "id": "plugin_interface_t", - "label": "plugin_interface_t", - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "plugin_interface_t", - "source_file": "", - "source_location": "" - }, - { - "id": "ref_ptr_t", - "label": "ref_ptr_t", - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "ref_ptr_t", - "source_file": "", - "source_location": "" - }, - { - "id": "render_pass_begin_signal", - "label": "render_pass_begin_signal", - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "render_pass_begin_signal", - "source_file": "", - "source_location": "" - }, - { - "id": "shared_ptr", - "label": "shared_ptr", - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "shared_ptr", - "source_file": "", - "source_location": "" - }, - { - "id": "tests_render_regression_render_test_render_test_t_count_passes", - "label": "count_passes", - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "count_passes", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L25" - }, - { - "id": "tests_render_regression_render_test_render_test_t_passes", - "label": "passes", - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "passes", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L24" - }, - { - "id": "tests_render_regression_render_test_render_test_t_rectangle", - "label": "rectangle", - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "rectangle", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L22" - }, - { - "id": "tests_render_regression_render_test_render_test_t_repo", - "label": "repo", - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "repo", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L21" - }, - { - "id": "tests_render_regression_render_test_render_test_t_solids", - "label": "solids", - "_origin": "ast", - "community": 3, - "community_name": "render_test_t", - "file_type": "code", - "norm_label": "solids", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L23" - }, - { - "id": "tests_render_regression_dynamic_check", - "label": "dynamic-check.py", - "_origin": "ast", - "community": 4, - "community_name": "redraw-check.py", - "file_type": "code", - "norm_label": "dynamic-check.py", - "source_file": "tests/render-regression/dynamic-check.py", - "source_location": "L1" - }, - { - "id": "tests_render_regression_ipc", - "label": "ipc.py", - "_origin": "ast", - "community": 4, - "community_name": "redraw-check.py", - "file_type": "code", - "norm_label": "ipc.py", - "source_file": "tests/render-regression/ipc.py", - "source_location": "L1" - }, - { - "id": "tests_render_regression_redraw_check", - "label": "redraw-check.py", - "_origin": "ast", - "community": 4, - "community_name": "redraw-check.py", - "file_type": "code", - "norm_label": "redraw-check.py", - "source_file": "tests/render-regression/redraw-check.py", - "source_location": "L1" - }, - { - "id": "files_wayfire_0_11_0_cube_rendering_notes", - "label": "wayfire-0.11.0-cube-rendering-notes.md", - "_origin": "ast", - "community": 5, - "community_name": "wayfire-0.11.0-cube-rendering-notes.md", - "file_type": "document", - "node_kind": "page", - "norm_label": "wayfire-0.11.0-cube-rendering-notes.md", - "source_file": "files/wayfire-0.11.0-cube-rendering-notes.md", - "source_location": "L1" - }, - { - "id": "files_wayfire_0_11_0_cube_rendering_notes_cube_damage_and_layer_ordering_revision_3", - "label": "Cube damage and layer ordering, revision 3", - "_origin": "ast", - "community": 5, - "community_name": "wayfire-0.11.0-cube-rendering-notes.md", - "file_type": "document", - "node_kind": "heading", - "norm_label": "cube damage and layer ordering, revision 3", - "source_file": "files/wayfire-0.11.0-cube-rendering-notes.md", - "source_location": "L1" - }, - { - "id": "map", - "label": "map", - "_origin": "ast", - "community": 6, - "community_name": "map", - "file_type": "code", - "norm_label": "map", - "source_file": "", - "source_location": "" - }, - { - "id": "tests_render_regression_render_test", - "label": "render-test.cpp", - "_origin": "ast", - "community": 6, - "community_name": "map", - "file_type": "code", - "norm_label": "render-test.cpp", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L1" - }, - { - "id": "string", - "label": "string", - "_origin": "ast", - "community": 7, - "community_name": "string", - "file_type": "code", - "norm_label": "string", - "source_file": "", - "source_location": "" - }, - { - "id": "tests_render_regression_readme", - "label": "README.md", - "_origin": "ast", - "community": 8, - "community_name": "README.md", - "file_type": "document", - "node_kind": "page", - "norm_label": "readme.md", - "source_file": "tests/render-regression/README.md", - "source_location": "L1" - }, - { - "id": "tests_render_regression_readme_cube_repaint_and_stacking_regressions", - "label": "Cube repaint and stacking regressions", - "_origin": "ast", - "community": 8, - "community_name": "README.md", - "file_type": "document", - "node_kind": "heading", - "norm_label": "cube repaint and stacking regressions", - "source_file": "tests/render-regression/README.md", - "source_location": "L1" - }, - { - "id": "tests_render_regression_run", - "label": "run.py", - "_origin": "ast", - "community": 9, - "community_name": "run.py", - "file_type": "code", - "norm_label": "run.py", - "source_file": "tests/render-regression/run.py", - "source_location": "L1" - } - ], - "links": [ - { - "source": "tests_render_regression_redraw_check_solid", - "target": "tests_render_regression_ipc_ipc", - "relation": "calls", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "call", - "source_file": "tests/render-regression/redraw-check.py", - "source_location": "L16", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t_init", - "target": "string", - "relation": "calls", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "call", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L33", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "tests_render_regression_render_test_render_test_t_repo", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "field", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L21", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "tests_render_regression_render_test_render_test_t_rectangle", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "field", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L22", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "tests_render_regression_render_test_render_test_t_solids", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "field", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L23", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "tests_render_regression_render_test_render_test_t_passes", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "field", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L24", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "tests_render_regression_render_test_render_test_t_count_passes", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "field", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L25", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "ref_ptr_t", - "relation": "references", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "field", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L21", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "map", - "relation": "references", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "field", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L23", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "connection_t", - "relation": "references", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "field", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L25", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "method_repository_t", - "relation": "references", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "generic_arg", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L21", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "color_rect_view_t", - "relation": "references", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "generic_arg", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L23", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "shared_ptr", - "relation": "references", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "generic_arg", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L23", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "string", - "relation": "references", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "generic_arg", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L23", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "render_pass_begin_signal", - "relation": "references", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "generic_arg", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L25", - "weight": 1.0 - }, - { - "source": "tests_render_regression_dynamic_check", - "target": "tests_render_regression_ipc_ipc", - "relation": "imports", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "import", - "source_file": "tests/render-regression/dynamic-check.py", - "source_location": "L3", - "weight": 1.0 - }, - { - "source": "tests_render_regression_redraw_check", - "target": "tests_render_regression_ipc_ipc", - "relation": "imports", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "import", - "source_file": "tests/render-regression/redraw-check.py", - "source_location": "L4", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test", - "target": "map", - "relation": "imports", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "import", - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L10", - "weight": 1.0 - }, - { - "source": "tests_render_regression_dynamic_check", - "target": "tests_render_regression_ipc", - "relation": "imports_from", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "import", - "source_file": "tests/render-regression/dynamic-check.py", - "source_location": "L3", - "weight": 1.0 - }, - { - "source": "tests_render_regression_redraw_check", - "target": "tests_render_regression_ipc", - "relation": "imports_from", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "context": "import", - "source_file": "tests/render-regression/redraw-check.py", - "source_location": "L4", - "weight": 1.0 - }, - { - "source": "files_wayfire_0_10_1_transparent_cube_animation_notes", - "target": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L1", - "weight": 1.0 - }, - { - "source": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "target": "files_wayfire_0_10_1_transparent_cube_animation_notes_working_theory", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L14", - "weight": 1.0 - }, - { - "source": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "target": "files_wayfire_0_10_1_transparent_cube_animation_notes_minimum_target", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L27", - "weight": 1.0 - }, - { - "source": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "target": "files_wayfire_0_10_1_transparent_cube_animation_notes_goal", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L3", - "weight": 1.0 - }, - { - "source": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "target": "files_wayfire_0_10_1_transparent_cube_animation_notes_current_direction", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L55", - "weight": 1.0 - }, - { - "source": "files_wayfire_0_10_1_transparent_cube_animation_notes_transparent_cube_animation_compatibility_notes", - "target": "files_wayfire_0_10_1_transparent_cube_animation_notes_avoid_for_now", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.10.1-transparent-cube-animation-notes.md", - "source_location": "L65", - "weight": 1.0 - }, - { - "source": "files_wayfire_0_11_0_cube_rendering_notes", - "target": "files_wayfire_0_11_0_cube_rendering_notes_cube_damage_and_layer_ordering_revision_3", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-0.11.0-cube-rendering-notes.md", - "source_location": "L1", - "weight": 1.0 - }, - { - "source": "files_wayfire_session", - "target": "files_wayfire_session__entry", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session", - "source_location": "L1", - "weight": 1.0 - }, - { - "source": "files_wayfire_session_2", - "target": "files_wayfire_session_2__entry", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session-2", - "source_location": "L1", - "weight": 1.0 - }, - { - "source": "tests_render_regression_ipc", - "target": "tests_render_regression_ipc_ipc", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "tests/render-regression/ipc.py", - "source_location": "L9", - "weight": 1.0 - }, - { - "source": "tests_render_regression_readme", - "target": "tests_render_regression_readme_cube_repaint_and_stacking_regressions", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "tests/render-regression/README.md", - "source_location": "L1", - "weight": 1.0 - }, - { - "source": "tests_render_regression_redraw_check", - "target": "tests_render_regression_redraw_check_shot", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "tests/render-regression/redraw-check.py", - "source_location": "L11", - "weight": 1.0 - }, - { - "source": "tests_render_regression_redraw_check", - "target": "tests_render_regression_redraw_check_solid", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "tests/render-regression/redraw-check.py", - "source_location": "L14", - "weight": 1.0 - }, - { - "source": "tests_render_regression_redraw_check", - "target": "tests_render_regression_redraw_check_check", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "tests/render-regression/redraw-check.py", - "source_location": "L7", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test", - "target": "tests_render_regression_render_test_render_test_t", - "relation": "contains", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L19", - "weight": 1.0 - }, - { - "source": "files_wayfire_session", - "target": "files_wayfire_session_xdg_session_type", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session", - "source_location": "L12", - "weight": 1.0 - }, - { - "source": "files_wayfire_session", - "target": "files_wayfire_session_gdk_backend", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session", - "source_location": "L13", - "weight": 1.0 - }, - { - "source": "files_wayfire_session", - "target": "files_wayfire_session_qt_qpa_platform", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session", - "source_location": "L14", - "weight": 1.0 - }, - { - "source": "files_wayfire_session", - "target": "files_wayfire_session_moz_enable_wayland", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session", - "source_location": "L15", - "weight": 1.0 - }, - { - "source": "files_wayfire_session_2", - "target": "files_wayfire_session_2_xdg_session_type", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session-2", - "source_location": "L12", - "weight": 1.0 - }, - { - "source": "files_wayfire_session_2", - "target": "files_wayfire_session_2_gdk_backend", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session-2", - "source_location": "L13", - "weight": 1.0 - }, - { - "source": "files_wayfire_session_2", - "target": "files_wayfire_session_2_qt_qpa_platform", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session-2", - "source_location": "L14", - "weight": 1.0 - }, - { - "source": "files_wayfire_session_2", - "target": "files_wayfire_session_2_moz_enable_wayland", - "relation": "defines", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "files/wayfire-session-2", - "source_location": "L15", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "plugin_interface_t", - "relation": "inherits", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L19", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "tests_render_regression_render_test_render_test_t_fini", - "relation": "method", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L173", - "weight": 1.0 - }, - { - "source": "tests_render_regression_render_test_render_test_t", - "target": "tests_render_regression_render_test_render_test_t_init", - "relation": "method", - "_origin": "ast", - "confidence": "EXTRACTED", - "confidence_score": 1.0, - "source_file": "tests/render-regression/render-test.cpp", - "source_location": "L28", - "weight": 1.0 - } - ], - "hyperedges": [] -}
\ No newline at end of file diff --git a/gui-wm/wayfire/graphify-out/manifest.json b/gui-wm/wayfire/graphify-out/manifest.json deleted file mode 100644 index 1ada7cfe4e41..000000000000 --- a/gui-wm/wayfire/graphify-out/manifest.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "files/wayfire-session": { - "mtime": 1653360147.3630395, - "seen": 1789770744.5676844, - "ast_hash": "b3aeec97b74efdb33552772c7f04b00f", - "semantic_hash": "" - }, - "files/wayfire-session-2": { - "mtime": 1780641764.4558036, - "seen": 1789770744.5676875, - "ast_hash": "0b8e4dd2101110c3e6a3688b8b035251", - "semantic_hash": "" - }, - "files/wayfire-0.10.1-transparent-cube-animation-notes.md": { - "mtime": 1780715444.271489, - "seen": 1789770744.567689, - "ast_hash": "437c539443e80b8f7c072aff6a0bd371", - "semantic_hash": "" - }, - "tests/render-regression/dynamic-check.py": { - "mtime": 1789784619.219585, - "seen": 1789784690.570067, - "ast_hash": "1b3143cc22694a5289171a62ee61f44c", - "semantic_hash": "" - }, - "tests/render-regression/ipc.py": { - "mtime": 1789784619.219585, - "seen": 1789784690.5700681, - "ast_hash": "8a410f0a1e6a7eff9bcf877d82a80f44", - "semantic_hash": "" - }, - "tests/render-regression/redraw-check.py": { - "mtime": 1789784619.219585, - "seen": 1789784690.5700693, - "ast_hash": "92a042b34d3c55bbc945d0b4d6afa84e", - "semantic_hash": "" - }, - "tests/render-regression/render-test.cpp": { - "mtime": 1789784619.219585, - "seen": 1789784690.5700703, - "ast_hash": "a27de53b1c4dce0e7aa04e5ca235c224", - "semantic_hash": "" - }, - "tests/render-regression/run.py": { - "mtime": 1789784668.6247869, - "seen": 1789784690.5700707, - "ast_hash": "d1b8580bf9e8fe8ad3a2ae34fc5b4acb", - "semantic_hash": "" - }, - "files/wayfire-0.11.0-cube-rendering-notes.md": { - "mtime": 1789784668.6247869, - "seen": 1789784690.570073, - "ast_hash": "47ddd006aafc4d8b09b7d948e7a891ae", - "semantic_hash": "" - }, - "tests/render-regression/README.md": { - "mtime": 1789784668.6247869, - "seen": 1789784690.5700736, - "ast_hash": "a1d86d4b3b0617f2f94d97636e3640fd", - "semantic_hash": "" - } -}
\ No newline at end of file diff --git a/gui-wm/wayfire/tests/render-regression/README.md b/gui-wm/wayfire/tests/render-regression/README.md deleted file mode 100644 index aa6a1422b688..000000000000 --- a/gui-wm/wayfire/tests/render-regression/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Cube repaint and stacking regressions - -Run these tests as a regular desktop user after installing the package. They -start a separate headless Wayfire with three outputs and never enable autostart. -Pass a prepared source tree from the same Wayfire revision: the fixture needs -the private cube control signal declarations. - -```sh -python3 run.py --source /path/to/prepared/wayfire-0.11.0 -``` - -Requires the installed Wayfire development headers, oledsaver sampler plugin, -Alacritty, grim, wlr-randr, Python/Pillow, pkg-config and a C++ compiler. The GLES -render device defaults to `/dev/dri/renderD128`; override `WLR_RENDER_DRM_DEVICE` -if needed. The temporary fixture plugin is never installed. Logs and snapshots -remain in the temporary directory printed by the runner. - -The tests cover: - -- A settled cube schedules no continuous rendering on three static outputs. -- A partial repaint modifies only its requested pixels, including a negative - output position, scale 2, and a 90-degree output transform. -- A real Wayland window held during cube rotation stays below screensaver and - lock-layer surfaces. The lock test checks layer ordering, not lock protocol - authentication or security. -- Content changes, movement/resizing, removal and remapping update cached cube - textures. The live composition is compared with a fresh desktop render. - -With 0.11.0-r2, the static test produced 732 render passes per second across the -three outputs, partial rendering changed pixels outside every damage region, -and held windows appeared above both higher layers. The r3 patch brings those -three measurements to zero. `--baseline` reports the known stacking/damage -failures without aborting, for before/after checks. - -The fixture explicitly damages and disables synthetic rectangles on removal: -`color_rect_view_t::close()` emits an unmap signal but does not do the surface -damage/disable sequence that a real Wayland surface performs. diff --git a/gui-wm/wayfire/tests/render-regression/dynamic-check.py b/gui-wm/wayfire/tests/render-regression/dynamic-check.py deleted file mode 100644 index d64f4cc9cae6..000000000000 --- a/gui-wm/wayfire/tests/render-regression/dynamic-check.py +++ /dev/null @@ -1,15 +0,0 @@ -import time,io,subprocess -from PIL import Image,ImageChops,ImageStat -from ipc import ipc,ENV -subprocess.run(['wlr-randr','--output','HEADLESS-1','--custom-mode','640x480','--pos=-640,0'],env=ENV,check=True) -ipc('test/rectangle',{'show':True});ipc('test/cube',{}) -time.sleep(1.1) -for case in [{'show':True,'blue':True}, {'show':True,'blue':True,'moved':True}, {'show':False}, {'show':True}]: - ipc('test/rectangle',case);time.sleep(.2) - live=Image.open(io.BytesIO(subprocess.check_output(['grim','-o','HEADLESS-1','-'],env=ENV))).convert('RGB') - ipc('test/snapshot') - fresh=Image.open(ENV['XDG_RUNTIME_DIR'] + '/snapshot.ppm').convert('RGB') - error=ImageStat.Stat(ImageChops.difference(live,fresh)).mean - assert max(error)<2, (case,error) - print('Tracked damage vs fresh composition:',case,'mean channel error',error,flush=True) -ipc('test/cube',{'stop':True});ipc('test/rectangle',{'show':False}) diff --git a/gui-wm/wayfire/tests/render-regression/ipc.py b/gui-wm/wayfire/tests/render-regression/ipc.py deleted file mode 100644 index 1120399cd67c..000000000000 --- a/gui-wm/wayfire/tests/render-regression/ipc.py +++ /dev/null @@ -1,27 +0,0 @@ -import json -import os -import socket -import struct -from pathlib import Path - -ENV = os.environ.copy() - -def ipc(method, data=None): - with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: - sock.settimeout(10) - sock.connect(ENV['WAYFIRE_SOCKET']) - payload = json.dumps({'method': method, 'data': data or {}}).encode() - sock.sendall(struct.pack('<I', len(payload)) + payload) - def read(n): - result = b'' - while len(result) < n: - chunk = sock.recv(n - len(result)) - if not chunk: - raise RuntimeError('IPC disconnected') - result += chunk - return result - return json.loads(read(struct.unpack('<I', read(4))[0])) - -if __name__ == '__main__': - import sys - print(json.dumps(ipc(sys.argv[1], json.loads(sys.argv[2]) if len(sys.argv) > 2 else None), indent=2)) diff --git a/gui-wm/wayfire/tests/render-regression/redraw-check.py b/gui-wm/wayfire/tests/render-regression/redraw-check.py deleted file mode 100644 index a3d3e5257fdb..000000000000 --- a/gui-wm/wayfire/tests/render-regression/redraw-check.py +++ /dev/null @@ -1,63 +0,0 @@ -import io,sys,time,subprocess -from pathlib import Path -from PIL import Image -from ipc import ipc,ENV -baseline='--baseline' in sys.argv - -def check(ok, label, value): - print(label, value, 'PASS' if ok else 'FAIL', flush=True) - if not baseline: assert ok, (label,value) - -def shot(output): - return Image.open(io.BytesIO(subprocess.check_output(['grim','-o',output,'-'],env=ENV))).convert('RGB') - -def solid(key,layer,color,box=(0,0,640,480),output='HEADLESS-1'): - r,g,b,a=color;x,y,w,h=box - return ipc('test/solid',dict(key=key,layer=layer,r=r,g=g,b=b,a=a,x=x,y=y,w=w,h=h,output=output)) - -subprocess.run(['wlr-randr','--output','HEADLESS-1','--custom-mode','640x480','--pos=-640,0', - '--output','HEADLESS-2','--custom-mode','640x480','--scale','2','--pos=0,0', - '--output','HEADLESS-3','--custom-mode','640x480','--transform','90','--pos=320,0'],env=ENV,check=True) -for name in ['HEADLESS-1','HEADLESS-2','HEADLESS-3']: - ipc('test/cube',{'output':name}) -time.sleep(1.5) -ipc('test/stats');time.sleep(1) -value=ipc('test/stats')['passes'];check(value==0,'Settled render passes in one second',value) -for name in ['HEADLESS-1','HEADLESS-2','HEADLESS-3']: - value=ipc('test/partial',{'output':name}) - check(value['outside']==0 and value['inside']>0,'Damage clipping '+name,value) -for name in ['HEADLESS-1','HEADLESS-2','HEADLESS-3']: - ipc('test/cube',{'output':name,'stop':True}) - -# Real toplevel held during workspace rotation must stay below screensavers. -ipc('wayfire/set-config-options',{'cube/initial_animation':'3000ms linear'}) -log=(Path(ENV['XDG_RUNTIME_DIR']) / 'test-terminal.log').open('w') -terminal=subprocess.Popen(['alacritty','--config-file','/dev/null','--class','render-regression', - '-o','colors.primary.background="#00ff00"','-o','cursor.style.blinking="Never"', - '-e','sleep','120'],env=ENV,stdout=log,stderr=log) -try: - for _ in range(50): - views=[v for v in ipc('window-rules/list-views') if v.get('app-id')=='render-regression'] - if views:break - time.sleep(.1) - assert views - view=views[0] - outputs={o['id']:o for o in ipc('window-rules/list-outputs')} - output=outputs[view['output-id']]['name'] - solid('saver',6,(1.,0.,1.,1.),(0,0,2000,2000),output) - time.sleep(.2) - result=ipc('test/held',{'output':output});assert result['found'] and result['carried-out'] - time.sleep(.2) - pixels=list(shot(output).get_flattened_data()) - leaks=sum(p!=(255,0,255) for p in pixels) - check(leaks==0,'Held window above screensaver pixels',leaks) - solid('lock',7,(0.,0.,1.,1.),(0,0,2000,2000),output) - time.sleep(.1) - pixels=list(shot(output).get_flattened_data()) - leaks=sum(p!=(0,0,255) for p in pixels) - check(leaks==0,'Held window above lock pixels',leaks) -finally: - terminal.terminate();terminal.wait(timeout=5) - for k in ['saver','lock']:ipc('test/solid',{'key':k,'close':True}) - ipc('test/cube',{'output':output,'stop':True}) - ipc('wayfire/set-config-options',{'cube/initial_animation':'350ms circle'}) diff --git a/gui-wm/wayfire/tests/render-regression/render-test.cpp b/gui-wm/wayfire/tests/render-regression/render-test.cpp deleted file mode 100644 index ac2d9ccbce23..000000000000 --- a/gui-wm/wayfire/tests/render-regression/render-test.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include <wayfire/core.hpp> -#include <wayfire/output.hpp> -#include <wayfire/output-layout.hpp> -#include <wayfire/plugin.hpp> -#include <wayfire/compositor-view.hpp> -#include <wayfire/render-manager.hpp> -#include <wayfire/nonstd/wlroots-full.hpp> -#include <drm_fourcc.h> -#include <fstream> -#include <map> -#include <wayfire/scene-operations.hpp> -#include <wayfire/scene-render.hpp> -#include <wayfire/workspace-set.hpp> -#include <wayfire/opengl.hpp> -#include <wayfire/plugins/common/shared-core-data.hpp> -#include <wayfire/plugins/ipc/ipc-method-repository.hpp> -#include "cube-control-signal.hpp" - -class render_test_t : public wf::plugin_interface_t -{ - wf::shared_data::ref_ptr_t<wf::ipc::method_repository_t> repo; - std::shared_ptr<wf::color_rect_view_t> rectangle; - std::map<std::string, std::shared_ptr<wf::color_rect_view_t>> solids; - int passes = 0; - wf::signal::connection_t<wf::render_pass_begin_signal> count_passes = - [this] (auto) { ++passes; }; - public: - void init() override - { - wf::get_core().connect(&count_passes); - repo->register_method("test/partial", [] (wf::json_t data) - { - auto output = wf::get_core().output_layout->find_output(std::string(data["output"])); - auto g = output->get_layout_geometry(); - int w = output->handle->width, h = output->handle->height; - wf::auxilliary_buffer_t buffer; buffer.allocate({w, h}); - wf::render_target_t target{buffer}; target.geometry = g; - target.scale = output->handle->scale; target.wl_transform = output->handle->transform; - wf::render_pass_params_t params; - params.target = target; params.damage = g; params.background_color = {1, 0, 1, 1}; - params.flags = wf::RPASS_CLEAR_BACKGROUND | wf::RPASS_NO_PRESENTATION; - wf::render_pass_t::run(params); - unsigned depth; - wf::gles::run_in_context([&] { - wf::gles::bind_render_buffer(target); - glGenRenderbuffers(1, &depth); glBindRenderbuffer(GL_RENDERBUFFER, depth); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth); - }); - std::vector<wf::scene::render_instance_uptr> instances; - wf::get_core().scene()->gen_render_instances(instances, [] (auto&) {}, output); - params.instances = &instances; - params.damage = wf::geometry_t{g.x + 10, g.y + 10, 20, 20}; - auto allowed = target.framebuffer_region_from_geometry_region(params.damage); - params.background_color = {0, 0, 0, 1}; - wf::render_pass_t::run(params); - wf::gles::run_in_context([&] { - wf::gles::bind_render_buffer(target); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); - glDeleteRenderbuffers(1, &depth); - }); - std::vector<uint32_t> pixels(w * h); - wlr_texture_read_pixels_options opts{}; - opts.data = pixels.data(); opts.format = DRM_FORMAT_ABGR8888; opts.stride = w * 4; - wlr_texture_read_pixels(buffer.get_texture(), &opts); - int outside = 0, inside = 0; - for (int y = 0; y < h; ++y) for (int x = 0; x < w; ++x) - if ((pixels[y * w + x] & 0x00ffffff) != 0x00ff00ff) - { - if (pixman_region32_contains_point(allowed.to_pixman(), x, y, nullptr)) ++inside; - else ++outside; - } - wf::json_t result; result["outside"] = outside; result["inside"] = inside; - return result; - }); - repo->register_method("test/held", [] (wf::json_t data) - { - auto output = wf::get_core().output_layout->find_output(std::string(data["output"])); - wayfire_toplevel_view view; - for (auto candidate : wf::get_core().get_all_views()) - if (candidate->get_output() == output && toplevel_cast(candidate)) - view = toplevel_cast(candidate); - cube_workspace_animation_signal signal{{1, 0}, output->wset()->get_current_workspace(), view}; - output->emit(&signal); - wf::json_t result; result["found"] = bool(view); result["carried-out"] = signal.carried_out; - return result; - }); - repo->register_method("test/stats", [this] (wf::json_t) - { - wf::json_t result; result["passes"] = passes; passes = 0; return result; - }); - repo->register_method("test/solid", [this] (wf::json_t data) - { - std::string key = data["key"]; - auto& solid = solids[key]; - if (data.has_member("close") && bool(data["close"])) - { - if (solid) { solid->close(); solid.reset(); } - return wf::json_t{}; - } - auto output = wf::get_core().output_layout->find_output(std::string(data["output"])); - if (!solid) - solid = wf::color_rect_view_t::create(wf::VIEW_ROLE_DESKTOP_ENVIRONMENT, output, - (wf::scene::layer)(int)data["layer"]); - solid->set_color({double(data["r"]), double(data["g"]), double(data["b"]), double(data["a"])}); - solid->set_geometry({double(data["x"]), double(data["y"]), double(data["w"]), double(data["h"])}); - solid->set_border(0); - return wf::json_t{}; - }); - repo->register_method("test/snapshot", [] (wf::json_t) - { - auto output = wf::get_core().output_layout->get_outputs().front(); - wf::auxilliary_buffer_t buffer; - auto g = output->get_layout_geometry(); - int w = output->handle->width, h = output->handle->height; - buffer.allocate({w, h}); - wf::render_target_t target{buffer}; - target.geometry = g; - target.scale = output->handle->scale; - target.wl_transform = output->handle->transform; - output->render->render_desktop(target); - std::vector<uint32_t> pixels(w * h); - wlr_texture_read_pixels_options opts{}; - opts.data = pixels.data(); opts.format = DRM_FORMAT_ABGR8888; opts.stride = w * 4; - wlr_texture_read_pixels(buffer.get_texture(), &opts); - std::ofstream f(std::string(getenv("XDG_RUNTIME_DIR")) + "/snapshot.ppm", std::ios::binary); - f << "P6\n" << w << " " << h << "\n255\n"; - for (auto p : pixels) { f.put(p & 255); f.put((p >> 8) & 255); f.put((p >> 16) & 255); } - return wf::json_t{}; - }); - repo->register_method("test/rectangle", [this] (wf::json_t data) - { - if (bool(data["show"])) - { - auto output = wf::get_core().output_layout->get_outputs().front(); - if (!rectangle) - rectangle = wf::color_rect_view_t::create(wf::VIEW_ROLE_TOPLEVEL, output, wf::scene::layer::WORKSPACE); - rectangle->set_color(data.has_member("blue") && bool(data["blue"]) ? - wf::color_t{0.0, 0.0, 1.0, 1.0} : wf::color_t{0.0, 1.0, 0.0, 1.0}); - rectangle->set_geometry(data.has_member("moved") && bool(data["moved"]) ? - wf::geometry_t{150, 120, 150, 120} : wf::geometry_t{50, 50, 400, 300}); - } else if (rectangle) - { - // color_rect_view_t::close() only emits the unmap signal; - // emulate a real surface unmap's damage and scene removal. - rectangle->damage(); - wf::scene::set_node_enabled(rectangle->get_root_node(), false); - rectangle->close(); rectangle.reset(); - } - return wf::json_t{}; - }); - repo->register_method("test/cube", [] (wf::json_t data) - { - wf::json_t result; - auto output = data.has_member("output") ? - wf::get_core().output_layout->find_output(std::string(data["output"])) : - wf::get_core().output_layout->get_outputs().front(); - double angle = data.has_member("angle") ? double(data["angle"]) : 0.65; - cube_control_signal signal{angle, 0.8, 0.0, - data.has_member("stop") && bool(data["stop"]), false}; - output->emit(&signal); - result["active"] = output->is_plugin_active("cube"); - result["carried-out"] = signal.carried_out; - return result; - }); - repo->register_method("test/inhibit", [] (wf::json_t data) - { - for (auto output : wf::get_core().output_layout->get_outputs()) - output->set_inhibited(bool(data["inhibit"])); - return wf::json_t{}; - }); - } - void fini() override - { - for (auto& [key, solid] : solids) if (solid) solid->close(); - repo->unregister_method("test/solid"); - repo->unregister_method("test/stats"); - repo->unregister_method("test/held"); - repo->unregister_method("test/partial"); - if (rectangle) { rectangle->close(); rectangle.reset(); } - repo->unregister_method("test/rectangle"); - repo->unregister_method("test/snapshot"); - repo->unregister_method("test/cube"); - repo->unregister_method("test/inhibit"); - } -}; -DECLARE_WAYFIRE_PLUGIN(render_test_t); diff --git a/gui-wm/wayfire/tests/render-regression/render-test.xml b/gui-wm/wayfire/tests/render-regression/render-test.xml deleted file mode 100644 index 2fef1dae8ab1..000000000000 --- a/gui-wm/wayfire/tests/render-regression/render-test.xml +++ /dev/null @@ -1 +0,0 @@ -<wayfire><plugin name="render-test"><_short>Isolated render tests</_short></plugin></wayfire> diff --git a/gui-wm/wayfire/tests/render-regression/run.py b/gui-wm/wayfire/tests/render-regression/run.py deleted file mode 100644 index a35f74553865..000000000000 --- a/gui-wm/wayfire/tests/render-regression/run.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python3 -"""Exercise the installed compositor in an isolated three-output session.""" -import argparse -import os -from pathlib import Path -import shlex -import shutil -import subprocess -import tempfile -import time - -parser = argparse.ArgumentParser() -parser.add_argument('--source', type=Path, required=True, - help='Prepared Wayfire source with the personal patches applied') -parser.add_argument('--baseline', action='store_true', help='Report failures without asserting') -args = parser.parse_args() -if os.geteuid() == 0: - parser.error('Run as a regular desktop user, not root') -tests = Path(__file__).resolve().parent -runtime = Path(tempfile.mkdtemp(prefix='wayfire-render-test-')) -flags = shlex.split(subprocess.check_output( - ['pkg-config', '--cflags', '--libs', 'wayfire', 'wf-utils'], text=True)) -subprocess.run(['c++', '-std=c++17', '-shared', '-fPIC', '-DWAYFIRE_PLUGIN', - '-DWLR_USE_UNSTABLE', '-I' + str(args.source / 'plugins/cube'), - str(tests / 'render-test.cpp'), '-o', str(runtime / 'librender-test.so'), - *flags], check=True) -shutil.copyfile(tests / 'render-test.xml', runtime / 'render-test.xml') -env = os.environ.copy() -for key in ['WAYLAND_DISPLAY', 'WAYFIRE_SOCKET', '_WAYFIRE_SOCKET', 'DISPLAY', - 'WAYFIRE_DEFAULT_CONFIG_BACKEND', 'WAYFIRE_CONFIG_FILE', 'LD_LIBRARY_PATH']: - env.pop(key, None) -env.update(XDG_RUNTIME_DIR=str(runtime), WLR_BACKENDS='headless', - WLR_HEADLESS_OUTPUTS='3', WLR_RENDERER='gles2', - WLR_RENDER_DRM_DEVICE=env.get('WLR_RENDER_DRM_DEVICE', '/dev/dri/renderD128'), - WAYFIRE_PLUGIN_PATH=str(runtime), WAYFIRE_PLUGIN_XML_PATH=str(runtime)) -config = runtime / 'wayfire.ini' -config.write_text('[core]\nplugins = ipc ipc-rules cube oledsaver-sampler render-test\n' - 'xwayland = false\nvwidth = 3\nvheight = 1\n') -with (runtime / 'wayfire.log').open('w') as log: - process = subprocess.Popen(['wayfire', '-c', str(config)], env=env, stdout=log, stderr=log) - try: - for _ in range(100): - if process.poll() is not None: - raise RuntimeError(f'Wayfire exited; see {runtime}/wayfire.log') - sockets = list(runtime.glob('wayfire-*.socket')) - displays = [p for p in runtime.glob('wayland-*') if not p.name.endswith('.lock')] - if sockets and displays: - env['WAYFIRE_SOCKET'] = str(sockets[0]) - env['WAYLAND_DISPLAY'] = displays[0].name - break - time.sleep(.1) - else: - raise RuntimeError('Timed out waiting for compositor') - time.sleep(.3) - subprocess.run(['python3', str(tests / 'dynamic-check.py')], env=env, check=True) - subprocess.run(['python3', str(tests / 'redraw-check.py'), - *(['--baseline'] if args.baseline else [])], env=env, check=True) - finally: - process.terminate() - try: - process.wait(timeout=10) - except subprocess.TimeoutExpired: - process.kill() - process.wait() - print(f'Test artifacts: {runtime}') diff --git a/gui-wm/wayfire/wayfire-0.11.0-r3.ebuild b/gui-wm/wayfire/wayfire-0.11.0-r3.ebuild deleted file mode 100644 index e4ed70cf47cd..000000000000 --- a/gui-wm/wayfire/wayfire-0.11.0-r3.ebuild +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright 2019-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -inherit dot-a meson toolchain-funcs - -DESCRIPTION="compiz like 3D wayland compositor" -HOMEPAGE="https://github.com/WayfireWM/wayfire" - -if [[ ${PV} == 9999 ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/WayfireWM/${PN}.git" - SLOT="0/0.12" - - IUSE="vulkan" - - # vulkan support is experimental and uses a bundled fork of wlroots - # keep it only in the live ebuild for now - RDEPEND="vulkan? ( media-libs/vulkan-loader )" - DEPEND="vulkan? ( dev-util/vulkan-headers )" - COMMON_DEPEND=" - !vulkan? ( - gui-libs/wlroots:0.20[drm(+),libinput(+),x11-backend,X?] - ) - vulkan? ( - dev-libs/libliftoff - media-libs/lcms:2 - media-libs/libdisplay-info:= - media-libs/mesa - sys-auth/seatd:= - x11-libs/libdrm - x11-libs/xcb-util-errors - x11-libs/xcb-util-renderutil - x11-libs/xcb-util-wm - ) - " -else - SRC_URI="https://github.com/WayfireWM/${PN}/releases/download/v${PV}/${P}.tar.xz" - KEYWORDS="~amd64 ~arm64 ~riscv" - SLOT="0/$(ver_cut 1-2)" - - COMMON_DEPEND="gui-libs/wlroots:0.20[drm(+),libinput(+),x11-backend,X?]" -fi - -LICENSE="MIT" -IUSE+=" X +dbus +gles3 nls openmp test" -RESTRICT="!test? ( test )" - -# bundled wlroots has the following dependency string according to included headers. -# wlroots[drm,gles2-renderer,libinput,x11-backend?,X?] -# enable x11-backend with X and vice versa -COMMON_DEPEND+=" - dev-cpp/nlohmann_json - dev-libs/glib:2 - dev-libs/libevdev - dev-libs/libinput:= - dev-libs/wayland - dev-libs/yyjson:= - >=dev-libs/wayland-protocols-1.12 - gui-libs/wf-config:${SLOT} - media-libs/glm - media-libs/libglvnd - media-libs/libjpeg-turbo:= - media-libs/libpng:= - virtual/libudev:= - x11-libs/cairo - x11-libs/libxkbcommon - x11-libs/pango - x11-libs/pixman - dbus? ( sys-apps/dbus ) - X? ( x11-libs/libxcb:= ) -" - -RDEPEND+=" - ${COMMON_DEPEND} - x11-misc/xkeyboard-config -" -DEPEND+=" - ${COMMON_DEPEND} - test? ( dev-cpp/doctest ) -" -BDEPEND=" - dev-util/wayland-scanner - virtual/pkgconfig - openmp? ( - || ( - sys-devel/gcc[openmp] - llvm-runtimes/clang-runtime[openmp] - ) - ) - nls? ( - sys-devel/gettext - ) -" - -PATCHES=( - "${FILESDIR}"/${P}-transparent-cube.patch - "${FILESDIR}"/${P}-rotate-layer-shell.patch - "${FILESDIR}"/${P}-screensaver-layer.patch - "${FILESDIR}"/${P}-desktop-sampling.patch - "${FILESDIR}"/${P}-cube-damage-stacking.patch -) - -# SCREENSAVER changes scene layer indices. Rebuild plugins against the matching -# ABI; wf-config above retains its upstream 0.11 subslot. -SLOT="${SLOT}.screensaver1" - -pkg_pretend() { - [[ ${MERGE_TYPE} != binary ]] && use openmp && tc-check-openmp -} - -pkg_setup() { - [[ ${MERGE_TYPE} != binary ]] && use openmp && tc-check-openmp -} - -src_prepare() { - default - - local dbusrunsession=$(usev dbus dbus-run-session) - sed -e "s:@EPREFIX@:${EPREFIX}:g" -e "s:@DBUS_RUN_SESSION@:${dbusrunsession}:" \ - "${FILESDIR}"/wayfire-session-2 > "${T}"/wayfire-session || die - sed -e "s:@EPREFIX@:${EPREFIX}:" \ - "${FILESDIR}"/wayfire-session.desktop > "${T}"/wayfire-session.desktop || die -} - -src_configure() { - lto-guarantee-fat - - local emesonargs=( - $(meson_feature test tests) - $(meson_feature X xwayland) - $(meson_use gles3 enable_gles32) - $(meson_feature nls build_locales) - $(meson_use openmp enable_openmp) - -Duse_system_wfconfig=enabled - ) - - if [[ ${PV} == 9999 ]]; then - emesonargs+=( - $(meson_feature !vulkan use_system_wlroots) - $(meson_use vulkan vulkan_effects) - ) - else - emesonargs+=( - -Duse_system_wlroots=enabled - -Dvulkan_effects=false - ) - fi - - meson_src_configure -} - -src_install() { - meson_src_install - dobin "${T}"/wayfire-session - - insinto "/usr/share/wayland-sessions/" - insopts -m644 - doins wayfire.desktop - doins "${T}"/wayfire-session.desktop - - insinto "/usr/share/wayfire/" - doins wayfire.ini - - insinto "/etc" - doins "${FILESDIR}"/wayfire.env - - strip-lto-bytecode -} |
