Wayland cOMPositor written in C++ using Louvre.
at main 5.9 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 "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}