My omnium-gatherom of scripts and source code.
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 "WToplevel.hpp"
15#include "global.hpp"
16#include "prelude.hpp"
17#include <LCursor.h>
18#include <LSceneView.h>
19
20WSurface::WSurface(Handle<Params> params) noexcept
21 : LSurface(params), view(this, &G::compositor()->surfacesLayer)
22{
23 view.enableParentOffset(false);
24}
25
26WSurface::~WSurface() noexcept
27{
28 // destroy thumbnails
29 while (not topBarItems.empty())
30 delete topBarItems.back();
31
32 // destroy thumbnail texture
33 if (thumbnail != nullptr)
34 delete thumbnail;
35}
36
37void WSurface::roleChanged() noexcept
38{
39 // hide cursor surfaces before we use LCursor
40 if (cursorRole()) {
41 view.setVisible(false);
42
43 } else if (dndIcon()) { // move drag & drop icons to the cursor layer so
44 // they always appear above other views
45 // ensure it is positioned behind 'softwareCursor'
46 view.setParent(&G::compositor()->cursorLayer);
47 view.insertAfter(nullptr, false);
48 setPos(cursor()->pos());
49 }
50}
51
52void WSurface::orderChanged() noexcept
53{
54 var prev = Handle<WSurface>(prevSurface());
55
56 // if prev has a different parent view keep searching for a match
57 while (prev != nullptr && prev->view.parent() != view.parent())
58 prev = Handle<WSurface>(prev->prevSurface());
59
60 // if prev is valid that means it has the same parent view. So we insert
61 // view after prev's view
62 if (prev != nullptr) {
63 view.insertAfter(&prev->view, false);
64 } else { // if there is no prev surface, insert it at the beginning of
65 // the current parent's children list
66 view.insertAfter(nullptr, false);
67 }
68}
69
70void WSurface::minimizedChanged() noexcept
71{
72 // remove pointer and keyboard focus
73 if (minimized()) {
74 if (hasPointerFocus())
75 seat()->pointer()->setFocus(nullptr);
76
77 if (hasKeyboardFocus())
78 seat()->keyboard()->setFocus(nullptr);
79 }
80
81 /*
82 * When a surface is minimized, all its children are also minimized.
83 * We only want to display toplevels in the top bar so we ignore the
84 * rest.
85 */
86
87 if (not toplevel()) {
88 view.setVisible(not minimized());
89 return;
90 }
91
92 /*
93 * would be nice if this piece of code was more expressive
94 * (functions?) TODO
95 */
96 if (minimized()) {
97 minimizedOutput = findPrimaryOutput();
98
99 if (minimizedOutput != nullptr) {
100 /*
101 * Save the current surface position relative to the
102 * output position as a percentage so we can restore it
103 * later even if the outputs arrangement changes or the
104 * given output is no longer available.
105 */
106 let localPos = rolePos() - minimizedOutput->pos();
107 prevMinimizedPos =
108 localPos / LSizeF(minimizedOutput->size());
109 } else {
110 /*
111 * In case the surface is not visible on any output, we
112 * select the first available output and position the
113 * surface 1/4 of the output as a fallback.
114 */
115 minimizedOutput = G::outputs().front();
116 prevMinimizedPos = LPointF(0.25f, 0.25f);
117 }
118
119 var tmpScene = LSceneView(sizeB(), bufferScale());
120 capture(&tmpScene);
121
122 // scale it to the thumbnail size
123 thumbnail = tmpScene.texture()->copyB(
124 LSize((THUMBNAIL_HEIGHT * sizeB().w()) / sizeB().h(),
125 THUMBNAIL_HEIGHT) *
126 2);
127
128 // create a top bar item for each top bar
129 for (let output : G::outputs())
130 new WTopBarItem(output->topBar.get(), this);
131 } else {
132 // if minimized output is nullptr, it's because it was
133 // uninitialized while the surface was minimized
134 if (minimizedOutput == nullptr) {
135 minimizedOutput = G::outputs().front();
136 prevMinimizedPos = LPointF(0.25f, 0.25f);
137 }
138
139 // destroy thumbnails
140 while (not topBarItems.empty())
141 delete topBarItems.front();
142
143 // restore back the previous unminimized position
144 setPos(minimizedOutput->pos() +
145 (prevMinimizedPos * minimizedOutput->size()));
146 minimizedOutput = nullptr;
147 view.setVisible(true);
148
149 // stack the surface above the rest
150 raise();
151 }
152}
153
154fn WSurface::findPrimaryOutput() const noexcept -> Handle<WOutput>
155{
156 var bestOutput = Handle<WOutput>(nullptr);
157 var bestArea = 0;
158 var surfaceRect = LRect();
159
160 // ignore the decoration of toplevel and pop-up roles
161 if (toplevel())
162 surfaceRect =
163 LRect(rolePos() + toplevel()->windowGeometry().pos(),
164 toplevel()->windowGeometry().size());
165 else if (popup())
166 surfaceRect = LRect(rolePos() + popup()->windowGeometry().pos(),
167 popup()->windowGeometry().size());
168 else
169 surfaceRect = LRect(rolePos(), size());
170
171 /*
172 * Calculate the area of the surface intersected with each output and
173 * return the one with the largest area
174 */
175 for (let output : G::outputs()) {
176 // we use LRegion to intersect both rects
177 var tmpRegion = LRegion();
178 tmpRegion.addRect(surfaceRect);
179 tmpRegion.clip(output->rect());
180
181 let extents = tmpRegion.extents();
182 let area =
183 (extents.x2 - extents.x1) * (extents.y2 - extents.y1);
184
185 if (area > bestArea) {
186 bestArea = area;
187 bestOutput = output;
188 }
189 }
190
191 return bestOutput;
192}
193
194void WSurface::capture(Handle<LSceneView> sceneView)
195{
196 /*
197 * Instead of moving each view to the sceneView, we move the scene to
198 * the views' position. This is why disabling parent offset is required.
199 */
200 sceneView->setPos(rolePos());
201
202 /*
203 * Add the view and any child subsurface view to the scene. Notice that
204 * we exclude child surfaces with the popup or toplevel roles.
205 */
206 G::moveSurfaceWithChildren(this, sceneView, G::SubSurfacesOnly::on);
207 sceneView->render();
208
209 // restore views to the surface layer
210 while (not sceneView->children().empty())
211 sceneView->children().front()->setParent(
212 &G::compositor()->surfacesLayer);
213}
214
215void WSurface::mappingChanged() noexcept
216{
217 if (mapped() and firstMapping) {
218 firstMapping = false;
219
220 // we only center toplevels the first time they are mapped
221 if (tl() != nullptr) {
222 // use the output where the cursor is currently located
223 let output = cursor()->output();
224
225 // restrict the area to the output size - the topbar
226 let availableSize =
227 output->size() - LSize(0, TOPBAR_HEIGHT);
228
229 // center the toplevel
230 setPos((output->pos() + LPoint(0, TOPBAR_HEIGHT)) +
231 (availableSize - tl()->windowGeometry().size()) /
232 2);
233
234 // prevent positioning it under the topbar (when the
235 // toplevel height is greater than dstSize.h())
236 if (pos().y() < output->pos().y() + TOPBAR_HEIGHT)
237 setY(output->pos().y() + TOPBAR_HEIGHT);
238
239 output->repaint();
240 }
241
242 raise();
243 }
244
245 // note LSurfaceView's are automatically hidden when their surfaces are
246 // unmapped
247 compositor()->repaintAllOutputs();
248}