My omnium-gatherom of scripts and source code.
at main 248 lines 6.8 kB view raw
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}