summaryrefslogtreecommitdiff
path: root/gui-wm
diff options
context:
space:
mode:
authorroot <root@alpha.trunkmasters.com>2026-09-19 03:01:08 -0500
committerroot <root@alpha.trunkmasters.com>2026-09-19 03:01:08 -0500
commitfc641028004011c6d8f1a4db9cd571823b825eab (patch)
treebc02a7e2a487eb8c1f405156d0c44b2f377663f5 /gui-wm
parent89051df130b5120fda29380e3b3b2e4effc349f8 (diff)
downloadbaldeagleos-repo-fc641028004011c6d8f1a4db9cd571823b825eab.tar.gz
baldeagleos-repo-fc641028004011c6d8f1a4db9cd571823b825eab.tar.xz
baldeagleos-repo-fc641028004011c6d8f1a4db9cd571823b825eab.zip
Checkpoint dirty output before beosrepo update
Diffstat (limited to 'gui-wm')
-rw-r--r--gui-wm/wayfire/Manifest1
-rw-r--r--gui-wm/wayfire/files/0001-added-get_previous_layout-to-output-layout.patch56
-rw-r--r--gui-wm/wayfire/files/0002-Added-previous_output-and-previous_output_with_win-t.patch133
-rw-r--r--gui-wm/wayfire/files/0003-prev-output-now-wraps-appropriately.patch44
-rw-r--r--gui-wm/wayfire/files/oswitch.patch233
-rw-r--r--gui-wm/wayfire/files/regex.patch21
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.0-fix-musl.patch28
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube-animation-notes.md69
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch1370
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.bad-unified-overlay-hold-20260605-0600441206
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.no-face-shading-20260605-044923928
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-all-window-alpha-080-output-local-hold-20260605-0619151209
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-anchor-backup-20260605-043727878
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-cube-overlay-disappear-cleanup-20260605-0812561290
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-cube-overlay-removes-alpha-on-clear-20260605-0622091209
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-cube-reuse-held-dim-20260605-0520281135
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-delta-normalize-backup-20260605-043931898
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-direct-modifier-dim-20260605-0509181123
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-disable-fixed-view-normal-node-20260605-0527201182
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-disable-vswitch-overlay-alpha-20260605-0523291143
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-empty-vswitch-cube-20260605-045314928
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-hold-070-moving-090-20260605-0607291193
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-hold-opacity-20260605-050310956
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-initial-080-moving-080-post-090-20260605-0612181193
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-keep-dim-during-arrow-20260605-0514591136
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-keep-post-move-overlay-20260605-0631421279
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-lighting-backup-20260605-044635915
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-move-window-opacity-20260605-045816941
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-moving-alpha-070-hold-090-20260605-0603541193
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-moving-alpha-080-hold-090-20260605-0601551193
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-moving-window-alpha-040-20260605-0529191192
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-output-local-hold-overlay-20260605-0617011193
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-overlay-backup-20260605-043031750
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-perceptual-moving-alpha-040-20260605-0539051182
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-post-move-dim-owned-by-cube-20260605-0626511211
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-remove-cube-overlay-dim-20260605-0511511151
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-revert-disappear-cleanup-20260605-0849471330
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-unified-overlay-hold-20260605-0554391193
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-unified-window-alpha-050-20260605-0534321183
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-vswitch-modifier-binding-20260605-0505581098
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-window-alpha-030-20260605-0543561183
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-window-alpha-070-single-overlay-20260605-0548051183
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-window-alpha-090-20260605-0550091193
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.pre-wrap-backup-20260605-043446874
-rw-r--r--gui-wm/wayfire/files/wayfire-0.10.1-transparent-cube.patch.working-backup-20260605-035448634
-rw-r--r--gui-wm/wayfire/files/wayfire-0.11.0-cube-damage-stacking.patch188
-rw-r--r--gui-wm/wayfire/files/wayfire-0.11.0-cube-rendering-notes.md30
-rw-r--r--gui-wm/wayfire/files/wayfire-0.11.0-desktop-sampling.patch288
-rw-r--r--gui-wm/wayfire/files/wayfire-0.11.0-rotate-layer-shell.patch123
-rw-r--r--gui-wm/wayfire/files/wayfire-0.11.0-screensaver-layer.patch16
-rw-r--r--gui-wm/wayfire/files/wayfire-0.11.0-transparent-cube.patch1395
-rw-r--r--gui-wm/wayfire/files/wayfire-session17
-rw-r--r--gui-wm/wayfire/files/wayfire-session-226
-rw-r--r--gui-wm/wayfire/files/wayfire-session.desktop7
-rw-r--r--gui-wm/wayfire/files/wayfire.env2
-rw-r--r--gui-wm/wayfire/files/windowrules.patch188
-rw-r--r--gui-wm/wayfire/graphify-out/.graphify_labels.json12
-rw-r--r--gui-wm/wayfire/graphify-out/.graphify_labels.json.sig1
-rw-r--r--gui-wm/wayfire/graphify-out/.graphify_root1
-rw-r--r--gui-wm/wayfire/graphify-out/2026-09-18/.graphify_labels.json5
-rw-r--r--gui-wm/wayfire/graphify-out/2026-09-18/GRAPH_REPORT.md53
-rw-r--r--gui-wm/wayfire/graphify-out/2026-09-18/graph.json450
-rw-r--r--gui-wm/wayfire/graphify-out/2026-09-18/manifest.json20
-rw-r--r--gui-wm/wayfire/graphify-out/GRAPH_REPORT.md73
-rw-r--r--gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/05eafb6a3d31ffc3daa8668c07dd7a92234e8056dfbaf507b2d06c4c0ce29adc.json1
-rw-r--r--gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/16b475fe1869b41ccaca5639bc87dfc15fe69c3ffe5edf9d9fd2b7e7b6985901.json1
-rw-r--r--gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/3043830d9822a41238110bbd2ec25f6ef2931793959d60403d4a33ce3aa5e684.json1
-rw-r--r--gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/656d91283bfb50cf8c84cfcefe5833d9ff9d753ce2ca9343a63d3710335a95ef.json1
-rw-r--r--gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/7c7bd637f54d282130e0eb0462a0d558e96532feae972902a0159f8240148bb0.json1
-rw-r--r--gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/afd3dd0924bb0887d234a6470a4a582419a372ff02d515773236ee07980f3c86.json1
-rw-r--r--gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/b09805d473c41ea8845c8a1783d0cccc23e4c6a9e1cb3f1a8e5a7fdb0e559d25.json1
-rw-r--r--gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/c14d817ea53b36a9a95614c9a0f8afaab9b57168c25b4425d4fbbb7336c95a13.json1
-rw-r--r--gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/e7aa91da4cf87f92e080fc64045b02641a5e02fe4f146744c6d14105840d38a3.json1
-rw-r--r--gui-wm/wayfire/graphify-out/cache/ast/v0.9.53-s2/f94d994c893c3549e0e32a67eed8bfde5b780f56890086c4daa8815d3dc0d4ad.json1
-rw-r--r--gui-wm/wayfire/graphify-out/cache/last_query_stamp1
-rw-r--r--gui-wm/wayfire/graphify-out/cache/stat-index.json1
-rw-r--r--gui-wm/wayfire/graphify-out/graph.html345
-rw-r--r--gui-wm/wayfire/graphify-out/graph.json1142
-rw-r--r--gui-wm/wayfire/graphify-out/manifest.json62
-rw-r--r--gui-wm/wayfire/tests/render-regression/README.md37
-rw-r--r--gui-wm/wayfire/tests/render-regression/dynamic-check.py15
-rw-r--r--gui-wm/wayfire/tests/render-regression/ipc.py27
-rw-r--r--gui-wm/wayfire/tests/render-regression/redraw-check.py63
-rw-r--r--gui-wm/wayfire/tests/render-regression/render-test.cpp187
-rw-r--r--gui-wm/wayfire/tests/render-regression/render-test.xml1
-rw-r--r--gui-wm/wayfire/tests/render-regression/run.py65
-rw-r--r--gui-wm/wayfire/wayfire-0.11.0-r3.ebuild170
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>&lt;super&gt; &lt;shift&gt; 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>&lt;super&gt; 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>&lt;super&gt; &lt;shift&gt; 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>&lt;super&gt; &lt;shift&gt; 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>&lt;super&gt; 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>&lt;super&gt; &lt;shift&gt; 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 &middot; 46 edges &middot; 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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
-}
-
-// 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
-}