Wayland cOMPositor written in C++ using Louvre.
1/* ========================================================================
2 *
3 * Filename: WSurface.cpp
4 * Description: W Compositor surface class definitions
5 * GitHub Repo: https://github.com/diego-est/womp
6 * Author: Diego A. Estrada Rivera
7 * License: GPL-3.0
8 *
9 * ======================================================================== */
10#include "WSurface.hpp"
11#include "WCompositor.hpp"
12#include "WOutput.hpp"
13#include "WTopBarItem.hpp"
14#include "global.hpp"
15#include "prelude.hpp"
16#include <LCursor.h>
17#include <LSceneView.h>
18
19WSurface::WSurface(Handle<Params> params) noexcept
20 : LSurface(params), view(this, &G::compositor()->surfacesLayer)
21{
22 view.enableParentOffset(false);
23}
24
25WSurface::~WSurface() noexcept
26{
27 // destroy thumbnails
28 while (not topBarItems.empty())
29 delete topBarItems.back();
30
31 // destroy thumbnail texture
32 if (thumbnail != nullptr)
33 delete thumbnail;
34}
35
36void WSurface::roleChanged() noexcept
37{
38 // hide cursor surfaces before we use LCursor
39 if (cursorRole()) {
40 view.setVisible(false);
41
42 } else if (dndIcon()) { // move drag & drop icons to the cursor layer so
43 // they always appear above other views
44 // ensure it is positioned behind 'softwareCursor'
45 view.setParent(&G::compositor()->cursorLayer);
46 view.insertAfter(nullptr, false);
47 setPos(cursor()->pos());
48 }
49}
50
51void WSurface::orderChanged() noexcept
52{
53 var prev = Handle<WSurface>(prevSurface());
54
55 // if prev has a different parent view keep searching for a match
56 while (prev != nullptr && prev->view.parent() != view.parent())
57 prev = Handle<WSurface>(prev->prevSurface());
58
59 // if prev is valid that means it has the same parent view. So we insert
60 // view after prev's view
61 if (prev != nullptr) {
62 view.insertAfter(&prev->view, false);
63 } else { // if there is no prev surface, insert it at the beginning of
64 // the current parent's children list
65 view.insertAfter(nullptr, false);
66 }
67}
68
69void WSurface::minimizedChanged() noexcept
70{
71 // remove pointer and keyboard focus
72 if (minimized()) {
73 if (hasPointerFocus())
74 seat()->pointer()->setFocus(nullptr);
75
76 if (hasKeyboardFocus())
77 seat()->keyboard()->setFocus(nullptr);
78 }
79
80 /*
81 * When a surface is minimized, all its children are also minimized.
82 * We only want to display toplevels in the top bar so we ignore the
83 * rest.
84 */
85
86 if (not toplevel()) {
87 view.setVisible(not minimized());
88 return;
89 }
90
91 /*
92 * would be nice if this piece of code was more expressive
93 * (functions?) TODO
94 */
95 if (minimized()) {
96 minimizedOutput = findPrimaryOutput();
97
98 if (minimizedOutput != nullptr) {
99 /*
100 * Save the current surface position relative to the
101 * output position as a percentage so we can restore it
102 * later even if the outputs arrangement changes or the
103 * given output is no longer available.
104 */
105 let localPos = rolePos() - minimizedOutput->pos();
106 prevMinimizedPos =
107 localPos / LSizeF(minimizedOutput->size());
108 } else {
109 /*
110 * In case the surface is not visible on any output, we
111 * select the first available output and position the
112 * surface 1/4 of the output as a fallback.
113 */
114 minimizedOutput = G::outputs().front();
115 prevMinimizedPos = LPointF(0.25f, 0.25f);
116 }
117
118 var tmpScene = LSceneView(sizeB(), bufferScale());
119 capture(&tmpScene);
120
121 // scale it to the thumbnail size
122 thumbnail = tmpScene.texture()->copyB(
123 LSize((THUMBNAIL_HEIGHT * sizeB().w()) / sizeB().h(),
124 THUMBNAIL_HEIGHT) *
125 2);
126
127 // create a top bar item for each top bar
128 for (let output : G::outputs())
129 new WTopBarItem(output->topBar.get(), this);
130 } else {
131 // if minimized output is nullptr, it's because it was
132 // uninitialized while the surface was minimized
133 if (minimizedOutput == nullptr) {
134 minimizedOutput = G::outputs().front();
135 prevMinimizedPos = LPointF(0.25f, 0.25f);
136 }
137
138 // destroy thumbnails
139 while (not topBarItems.empty())
140 delete topBarItems.front();
141
142 // restore back the previous unminimized position
143 setPos(minimizedOutput->pos() +
144 (prevMinimizedPos * minimizedOutput->size()));
145 minimizedOutput = nullptr;
146 view.setVisible(true);
147
148 // stack the surface above the rest
149 raise();
150 }
151}
152
153fn WSurface::findPrimaryOutput() const noexcept -> Handle<WOutput>
154{
155 var bestOutput = Handle<WOutput>(nullptr);
156 var bestArea = 0;
157 var surfaceRect = LRect();
158
159 // ignore the decoration of toplevel and pop-up roles
160 if (toplevel())
161 surfaceRect =
162 LRect(rolePos() + toplevel()->windowGeometry().pos(),
163 toplevel()->windowGeometry().size());
164 else if (popup())
165 surfaceRect = LRect(rolePos() + popup()->windowGeometry().pos(),
166 popup()->windowGeometry().size());
167 else
168 surfaceRect = LRect(rolePos(), size());
169
170 /*
171 * Calculate the area of the surface intersected with each output and
172 * return the one with the largest area
173 */
174 for (let output : G::outputs()) {
175 // we use LRegion to intersect both rects
176 var tmpRegion = LRegion();
177 tmpRegion.addRect(surfaceRect);
178 tmpRegion.clip(output->rect());
179
180 let extents = tmpRegion.extents();
181 let area =
182 (extents.x2 - extents.x1) * (extents.y2 - extents.y1);
183
184 if (area > bestArea) {
185 bestArea = area;
186 bestOutput = output;
187 }
188 }
189
190 return bestOutput;
191}
192
193void WSurface::capture(Handle<LSceneView> sceneView)
194{
195 /*
196 * Instead of moving each view to the sceneView, we move the scene to
197 * the views' position. This is why disabling parent offset is required.
198 */
199 sceneView->setPos(rolePos());
200
201 /*
202 * Add the view and any child subsurface view to the scene. Notice that
203 * we exclude child surfaces with the popup or toplevel roles.
204 */
205 G::moveSurfaceWithChildren(this, sceneView, G::SubSurfacesOnly::on);
206 sceneView->render();
207
208 // restore views to the surface layer
209 while (not sceneView->children().empty())
210 sceneView->children().front()->setParent(
211 &G::compositor()->surfacesLayer);
212}