···99 * ======================================================================== */
1010#include "WOutput.hpp"
1111#include "WCompositor.hpp"
1212+#include "WSurface.hpp"
1213#include "WTopBar.hpp"
1314#include "global.hpp"
1415#include "prelude.hpp"
···91929293void WOutput::uninitializeGL() noexcept
9394{
9595+ /*
9696+ * set minimized output to nullptr to prevent the compositor from
9797+ * crashing when a surface is unminimized and
9898+ * WSurface::minimizeChanged() is triggered
9999+ */
100100+ for (let surface : G::surfaces())
101101+ if (surface->minimizedOutput == this)
102102+ surface->minimizedOutput = nullptr;
103103+94104 G::scene()->handleUninitializeGL(this);
9510596106 if (wallpaperView->texture())
+164-7
src/WSurface.cpp
···88 *
99 * ======================================================================== */
1010#include "WSurface.hpp"
1111-#include "prelude.hpp"
1212-#include "global.hpp"
1311#include "WCompositor.hpp"
1212+#include "WOutput.hpp"
1313+#include "WTopBarItem.hpp"
1414+#include "global.hpp"
1515+#include "prelude.hpp"
1416#include <LCursor.h>
1717+#include <LSceneView.h>
15181919+WSurface::WSurface(Handle<Params> params) noexcept
2020+ : LSurface(params), view(this, &G::compositor()->surfacesLayer)
2121+{
2222+ view.enableParentOffset(false);
2323+}
16241717-WSurface::WSurface(Handle<Params> params) noexcept : LSurface(params),
1818- view(this, &G::compositor()->surfacesLayer)
2525+WSurface::~WSurface() noexcept
1926{
2020- view.enableParentOffset(false);
2727+ // destroy thumbnails
2828+ while (not topBarItems.empty())
2929+ delete topBarItems.back();
3030+3131+ // destroy thumbnail texture
3232+ if (thumbnail != nullptr)
3333+ delete thumbnail;
2134}
22352336void WSurface::roleChanged() noexcept
···2639 if (cursorRole()) {
2740 view.setVisible(false);
28412929- } else if (dndIcon()) { // move drag & drop icons to the cursor layer so they always appear above other views
4242+ } else if (dndIcon()) { // move drag & drop icons to the cursor layer so
4343+ // they always appear above other views
3044 // ensure it is positioned behind 'softwareCursor'
3145 view.setParent(&G::compositor()->cursorLayer);
3246 view.insertAfter(nullptr, false);
···4660 // view after prev's view
4761 if (prev != nullptr) {
4862 view.insertAfter(&prev->view, false);
4949- } else { // if there is no prev surface, insert it at the beginning of the current parent's children list
6363+ } else { // if there is no prev surface, insert it at the beginning of
6464+ // the current parent's children list
5065 view.insertAfter(nullptr, false);
5166 }
5267}
6868+6969+void WSurface::minimizedChanged() noexcept
7070+{
7171+ // remove pointer and keyboard focus
7272+ if (minimized()) {
7373+ if (hasPointerFocus())
7474+ seat()->pointer()->setFocus(nullptr);
7575+7676+ if (hasKeyboardFocus())
7777+ seat()->keyboard()->setFocus(nullptr);
7878+ }
7979+8080+ /*
8181+ * When a surface is minimized, all its children are also minimized.
8282+ * We only want to display toplevels in the top bar so we ignore the
8383+ * rest.
8484+ */
8585+8686+ if (not toplevel()) {
8787+ view.setVisible(not minimized());
8888+ return;
8989+ }
9090+9191+ if (minimized()) {
9292+ // find the output where the surface is currently most visible
9393+ minimizedOutput = primaryOutput();
9494+9595+ if (minimizedOutput != nullptr) {
9696+ /*
9797+ * Save the current surface position relative to the
9898+ * output position as a percentage so we can restore it
9999+ * later even if the outputs arrangement changes or the
100100+ * given output is no longer available.
101101+ */
102102+ let localPos = rolePos() - minimizedOutput->pos();
103103+ prevMinimizedPos =
104104+ localPos / LSizeF(minimizedOutput->size());
105105+ } else {
106106+ /*
107107+ * In case the surface is not visible on any output, we
108108+ * select the first available output and position the
109109+ * surface 1/4 of the output as a fallback.
110110+ */
111111+ minimizedOutput = G::outputs().front();
112112+ prevMinimizedPos = LPointF(0.25f, 0.25f);
113113+ }
114114+115115+ var tmpScene = LSceneView(sizeB(), bufferScale());
116116+ capture(&tmpScene);
117117+118118+ // scale it to the thumbnail size
119119+ thumbnail = tmpScene.texture()->copyB(
120120+ LSize((THUMBNAIL_HEIGHT * sizeB().w()) / sizeB().h(),
121121+ THUMBNAIL_HEIGHT) *
122122+ 2);
123123+124124+ // create a top bar item for each top bar
125125+ for (let output : G::outputs())
126126+ new WTopBarItem(output->topBar.get(), this);
127127+ } else {
128128+ // if minimized output is nullptr, it's because it was
129129+ // uninitialized while the surface was minimized
130130+ if (minimizedOutput == nullptr) {
131131+ minimizedOutput = G::outputs().front();
132132+ prevMinimizedPos = LPointF(0.25f, 0.25f);
133133+ }
134134+135135+ // destroy thumbnails
136136+ while (not topBarItems.empty())
137137+ delete topBarItems.front();
138138+139139+ // restory back the previous unminimized position
140140+ setPos(minimizedOutput->pos() +
141141+ (prevMinimizedPos * minimizedOutput->size()));
142142+ minimizedOutput = nullptr;
143143+ view.setVisible(true);
144144+145145+ // stack the surface above the rest
146146+ raise();
147147+ }
148148+}
149149+150150+fn WSurface::primaryOutput() const noexcept -> Handle<WOutput>
151151+{
152152+ var bestOutput = Handle<WOutput>(nullptr);
153153+ var bestArea = 0;
154154+ var surfaceRect = LRect();
155155+156156+ // ignore the decoration of toplevel and pop-up roles
157157+ if (toplevel())
158158+ surfaceRect =
159159+ LRect(rolePos() + toplevel()->windowGeometry().pos(),
160160+ toplevel()->windowGeometry().size());
161161+ else if (popup())
162162+ surfaceRect = LRect(rolePos() + popup()->windowGeometry().pos(),
163163+ popup()->windowGeometry().size());
164164+ else
165165+ surfaceRect = LRect(rolePos(), size());
166166+167167+ /*
168168+ * Calculate the area of the surface intersected with each output and
169169+ * return the one with the largest area
170170+ */
171171+ for (let output : G::outputs()) {
172172+ // we use LRegion to intersect both rects
173173+ var tmpRegion = LRegion();
174174+ tmpRegion.addRect(surfaceRect);
175175+ tmpRegion.clip(output->rect());
176176+177177+ let extents = tmpRegion.extents();
178178+ let area =
179179+ (extents.x2 - extents.x1) * (extents.y2 - extents.y1);
180180+181181+ if (area > bestArea) {
182182+ bestArea = area;
183183+ bestOutput = output;
184184+ }
185185+ }
186186+187187+ return bestOutput;
188188+}
189189+190190+void WSurface::capture(Handle<LSceneView> sceneView)
191191+{
192192+ /*
193193+ * Instead of moving each view to the sceneView, we move the scene to
194194+ * the views' position. This is why disabling parent offset is required.
195195+ */
196196+ sceneView->setPos(rolePos());
197197+198198+ /*
199199+ * Add the view and any child subsurface view to the scene. Notice that
200200+ * we exclude child surfaces with the popup or toplevel roles.
201201+ */
202202+ G::moveSurfaceWithChildren(this, sceneView, subSurfacesOnly::on);
203203+ sceneView->render();
204204+205205+ // restore views to the surface layer
206206+ while (not sceneView->children().empty())
207207+ sceneView->children().front()->setParent(
208208+ &G::compositor()->surfacesLayer);
209209+}
+22
src/WSurface.hpp
···14141515using namespace Louvre;
16161717+class WTopBarItem;
1818+class WOutput;
1919+1720class WSurface : public LSurface {
1821 public:
1922 WSurface(Handle<LSurface::Params> params) noexcept;
2323+ ~WSurface() noexcept;
20242125 void roleChanged() noexcept override;
2226 void orderChanged() noexcept override;
2727+ void minimizedChanged() noexcept override;
2828+2929+ // return the output where the surface is currently the most visible
3030+ fn primaryOutput() const noexcept -> Handle<WOutput>;
3131+3232+ // take a snapshot of the surface, including its subsurfaces
3333+ void capture(Handle<LSceneView> sceneView);
23342435 LSurfaceView view;
3636+3737+ // a single thumbnail texture shared by all top bar items
3838+ Handle<LTexture> thumbnail = nullptr;
3939+4040+ // list of thumbnail views, one for each output's topbar
4141+ std::list<Handle<WTopBarItem>> topBarItems;
4242+4343+ // the output where the surface was most visible before being minimized
4444+ Handle<WOutput> minimizedOutput = nullptr;
4545+4646+ LPointF prevMinimizedPos;
2547};
+20
src/WTopBar.cpp
···1010#include "WTopBar.hpp"
1111#include "WCompositor.hpp"
1212#include "WOutput.hpp"
1313+#include "WTopBarItem.hpp"
1314#include "global.hpp"
14151516WTopBar::WTopBar(Handle<WOutput> output) noexcept
1617 : output(output), view(0.f, 0.f, 0.f, 0.8f, &G::compositor()->overlayLayer)
1718{
1919+ // copy thumbnails from an already initialized output
2020+ for (let o : G::outputs()) {
2121+ if (o == output)
2222+ continue;
2323+2424+ for (let item : Ref<std::list<Handle<WTopBarItem>>>(
2525+ o->topBar->view.children()))
2626+ new WTopBarItem(this, item->surface);
2727+2828+ break;
2929+ }
1830 update();
1931}
2032···2840{
2941 view.setSize(output->size().w(), TOPBAR_HEIGHT);
3042 view.setPos(output->pos());
4343+4444+ // update thumbnails
4545+ for (var x = TOPBAR_PADDING;
4646+ let item : Ref<std::list<Handle<WTopBarItem>>>(view.children())) {
4747+ item->setPos(x, TOPBAR_PADDING);
4848+ x += item->size().w() + THUMBNAIL_MARGIN;
4949+ }
5050+3151 output->repaint();
3252}