File indexing completed on 2024-11-10 04:57:10
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2009 Michael Zanetti <michael_zanetti@gmx.net> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "slideback.h" 0011 #include "effect/effecthandler.h" 0012 0013 namespace KWin 0014 { 0015 0016 SlideBackEffect::SlideBackEffect() 0017 { 0018 m_tabboxActive = 0; 0019 m_justMapped = m_upmostWindow = nullptr; 0020 connect(effects, &EffectsHandler::windowAdded, this, &SlideBackEffect::slotWindowAdded); 0021 connect(effects, &EffectsHandler::windowDeleted, this, &SlideBackEffect::slotWindowDeleted); 0022 connect(effects, &EffectsHandler::tabBoxAdded, this, &SlideBackEffect::slotTabBoxAdded); 0023 connect(effects, &EffectsHandler::stackingOrderChanged, this, &SlideBackEffect::slotStackingOrderChanged); 0024 connect(effects, &EffectsHandler::tabBoxClosed, this, &SlideBackEffect::slotTabBoxClosed); 0025 0026 const auto windows = effects->stackingOrder(); 0027 for (EffectWindow *window : windows) { 0028 slotWindowAdded(window); 0029 } 0030 } 0031 0032 void SlideBackEffect::slotStackingOrderChanged() 0033 { 0034 if (effects->activeFullScreenEffect() || m_tabboxActive) { 0035 oldStackingOrder = effects->stackingOrder(); 0036 usableOldStackingOrder = usableWindows(oldStackingOrder); 0037 return; 0038 } 0039 0040 QList<EffectWindow *> newStackingOrder = effects->stackingOrder(), 0041 usableNewStackingOrder = usableWindows(newStackingOrder); 0042 if (usableNewStackingOrder == usableOldStackingOrder || usableNewStackingOrder.isEmpty()) { 0043 oldStackingOrder = newStackingOrder; 0044 usableOldStackingOrder = usableNewStackingOrder; 0045 return; 0046 } 0047 0048 m_upmostWindow = usableNewStackingOrder.last(); 0049 0050 if (m_upmostWindow == m_justMapped) { // a window was added, got on top, stacking changed. Nothing impressive 0051 m_justMapped = nullptr; 0052 } else if (!usableOldStackingOrder.isEmpty() && m_upmostWindow != usableOldStackingOrder.last()) { 0053 windowRaised(m_upmostWindow); 0054 } 0055 0056 oldStackingOrder = newStackingOrder; 0057 usableOldStackingOrder = usableNewStackingOrder; 0058 } 0059 0060 void SlideBackEffect::windowRaised(EffectWindow *w) 0061 { 0062 // Determine all windows on top of the activated one 0063 bool currentFound = false; 0064 for (EffectWindow *tmp : std::as_const(oldStackingOrder)) { 0065 if (!currentFound) { 0066 if (tmp == w) { 0067 currentFound = true; 0068 } 0069 } else { 0070 if (isWindowUsable(tmp) && tmp->isOnCurrentDesktop() && w->isOnCurrentDesktop()) { 0071 // Do we have to move it? 0072 if (intersects(w, tmp->frameGeometry().toRect())) { 0073 QRect slideRect; 0074 slideRect = getSlideDestination(getModalGroupGeometry(w), tmp->frameGeometry().toRect()); 0075 effects->setElevatedWindow(tmp, true); 0076 elevatedList.append(tmp); 0077 motionManager.manage(tmp); 0078 motionManager.moveWindow(tmp, slideRect); 0079 destinationList.insert(tmp, slideRect); 0080 coveringWindows.append(tmp); 0081 } else { 0082 // Does it intersect with a moved (elevated) window and do we have to elevate it too? 0083 for (EffectWindow *elevatedWindow : std::as_const(elevatedList)) { 0084 if (tmp->frameGeometry().intersects(elevatedWindow->frameGeometry())) { 0085 effects->setElevatedWindow(tmp, true); 0086 elevatedList.append(tmp); 0087 break; 0088 } 0089 } 0090 } 0091 } 0092 if (tmp->isDock() || tmp->keepAbove()) { 0093 effects->setElevatedWindow(tmp, true); 0094 elevatedList.append(tmp); 0095 } 0096 } 0097 } 0098 // If a window is minimized it could happen that the panels stay elevated without any windows sliding. 0099 // clear all elevation settings 0100 if (!motionManager.managingWindows()) { 0101 for (EffectWindow *tmp : std::as_const(elevatedList)) { 0102 effects->setElevatedWindow(tmp, false); 0103 } 0104 } 0105 } 0106 0107 QRect SlideBackEffect::getSlideDestination(const QRect &windowUnderGeometry, const QRect &windowOverGeometry) 0108 { 0109 // Determine the shortest way: 0110 int leftSlide = windowUnderGeometry.left() - windowOverGeometry.right() - 20; 0111 int rightSlide = windowUnderGeometry.right() - windowOverGeometry.left() + 20; 0112 int upSlide = windowUnderGeometry.top() - windowOverGeometry.bottom() - 20; 0113 int downSlide = windowUnderGeometry.bottom() - windowOverGeometry.top() + 20; 0114 0115 int horizSlide = leftSlide; 0116 if (std::abs(horizSlide) > std::abs(rightSlide)) { 0117 horizSlide = rightSlide; 0118 } 0119 int vertSlide = upSlide; 0120 if (std::abs(vertSlide) > std::abs(downSlide)) { 0121 vertSlide = downSlide; 0122 } 0123 0124 QRect slideRect = windowOverGeometry; 0125 if (std::abs(horizSlide) < std::abs(vertSlide)) { 0126 slideRect.moveLeft(slideRect.x() + horizSlide); 0127 } else { 0128 slideRect.moveTop(slideRect.y() + vertSlide); 0129 } 0130 return slideRect; 0131 } 0132 0133 void SlideBackEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) 0134 { 0135 int time = 0; 0136 if (m_lastPresentTime.count()) { 0137 time = (presentTime - m_lastPresentTime).count(); 0138 } 0139 m_lastPresentTime = presentTime; 0140 0141 if (motionManager.managingWindows()) { 0142 motionManager.calculate(time); 0143 data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; 0144 } 0145 0146 const QList<EffectWindow *> windows = effects->stackingOrder(); 0147 for (auto *w : windows) { 0148 w->setData(WindowForceBlurRole, QVariant(true)); 0149 } 0150 0151 effects->prePaintScreen(data, presentTime); 0152 } 0153 0154 void SlideBackEffect::postPaintScreen() 0155 { 0156 if (motionManager.areWindowsMoving()) { 0157 effects->addRepaintFull(); 0158 } 0159 0160 for (auto &w : effects->stackingOrder()) { 0161 w->setData(WindowForceBlurRole, QVariant()); 0162 } 0163 0164 effects->postPaintScreen(); 0165 } 0166 0167 void SlideBackEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) 0168 { 0169 if (motionManager.isManaging(w)) { 0170 data.setTransformed(); 0171 } 0172 0173 effects->prePaintWindow(w, data, presentTime); 0174 } 0175 0176 void SlideBackEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, QRegion region, WindowPaintData &data) 0177 { 0178 if (motionManager.isManaging(w)) { 0179 motionManager.apply(w, data); 0180 } 0181 for (const QRegion &r : std::as_const(clippedRegions)) { 0182 region = region.intersected(r); 0183 } 0184 effects->paintWindow(renderTarget, viewport, w, mask, region, data); 0185 clippedRegions.clear(); 0186 } 0187 0188 void SlideBackEffect::postPaintWindow(EffectWindow *w) 0189 { 0190 if (motionManager.isManaging(w)) { 0191 if (destinationList.contains(w)) { 0192 if (!motionManager.isWindowMoving(w)) { // has window reched its destination? 0193 // If we are still intersecting with the upmostWindow it is moving. slide to somewhere else 0194 // restore the stacking order of all windows not intersecting any more except panels 0195 if (coveringWindows.contains(w)) { 0196 QList<EffectWindow *> tmpList; 0197 for (EffectWindow *tmp : std::as_const(elevatedList)) { 0198 QRect elevatedGeometry = tmp->frameGeometry().toRect(); 0199 if (motionManager.isManaging(tmp)) { 0200 elevatedGeometry = motionManager.transformedGeometry(tmp).toAlignedRect(); 0201 } 0202 if (m_upmostWindow && !tmp->isDock() && !tmp->keepAbove() && m_upmostWindow->frameGeometry().intersects(elevatedGeometry)) { 0203 QRect newDestination; 0204 newDestination = getSlideDestination(getModalGroupGeometry(m_upmostWindow), elevatedGeometry); 0205 if (!motionManager.isManaging(tmp)) { 0206 motionManager.manage(tmp); 0207 } 0208 motionManager.moveWindow(tmp, newDestination); 0209 destinationList[tmp] = newDestination; 0210 } else { 0211 if (!tmp->isDock()) { 0212 bool keepElevated = false; 0213 for (EffectWindow *elevatedWindow : std::as_const(tmpList)) { 0214 if (tmp->frameGeometry().intersects(elevatedWindow->frameGeometry())) { 0215 keepElevated = true; 0216 } 0217 } 0218 if (!keepElevated) { 0219 effects->setElevatedWindow(tmp, false); 0220 elevatedList.removeAll(tmp); 0221 } 0222 } 0223 } 0224 tmpList.append(tmp); 0225 } 0226 } else { 0227 // Move the window back where it belongs 0228 motionManager.moveWindow(w, w->frameGeometry().toRect()); 0229 destinationList.remove(w); 0230 } 0231 } 0232 } else { 0233 // is window back at its original position? 0234 if (!motionManager.isWindowMoving(w)) { 0235 motionManager.unmanage(w); 0236 effects->addRepaintFull(); 0237 } 0238 } 0239 if (coveringWindows.contains(w)) { 0240 // It could happen that there is no aciveWindow() here if the user clicks the close-button on an inactive window. 0241 // Just skip... the window will be removed in windowDeleted() later 0242 if (m_upmostWindow && !intersects(m_upmostWindow, motionManager.transformedGeometry(w).toAlignedRect())) { 0243 coveringWindows.removeAll(w); 0244 if (coveringWindows.isEmpty()) { 0245 // Restore correct stacking order 0246 for (EffectWindow *tmp : std::as_const(elevatedList)) { 0247 effects->setElevatedWindow(tmp, false); 0248 } 0249 elevatedList.clear(); 0250 } 0251 } 0252 } 0253 } 0254 if (!isActive()) { 0255 m_lastPresentTime = std::chrono::milliseconds::zero(); 0256 } 0257 effects->postPaintWindow(w); 0258 } 0259 0260 void SlideBackEffect::slotWindowDeleted(EffectWindow *w) 0261 { 0262 if (w == m_upmostWindow) { 0263 m_upmostWindow = nullptr; 0264 } 0265 if (w == m_justMapped) { 0266 m_justMapped = nullptr; 0267 } 0268 usableOldStackingOrder.removeAll(w); 0269 oldStackingOrder.removeAll(w); 0270 coveringWindows.removeAll(w); 0271 elevatedList.removeAll(w); 0272 if (motionManager.isManaging(w)) { 0273 motionManager.unmanage(w); 0274 } 0275 } 0276 0277 void SlideBackEffect::slotWindowAdded(EffectWindow *w) 0278 { 0279 m_justMapped = w; 0280 0281 connect(w, &EffectWindow::minimizedChanged, this, [this, w]() { 0282 if (!w->isMinimized()) { 0283 slotWindowUnminimized(w); 0284 } 0285 }); 0286 } 0287 0288 void SlideBackEffect::slotWindowUnminimized(EffectWindow *w) 0289 { 0290 // SlideBack should not be triggered on an unminimized window. For this we need to store the last unminimized window. 0291 m_justMapped = w; 0292 // the stackingOrderChanged() signal came before the window turned an effect window 0293 // usually this is no problem as the change shall not be caught anyway, but 0294 // the window may have changed its stack position, bug #353745 0295 slotStackingOrderChanged(); 0296 } 0297 0298 void SlideBackEffect::slotTabBoxAdded() 0299 { 0300 ++m_tabboxActive; 0301 } 0302 0303 void SlideBackEffect::slotTabBoxClosed() 0304 { 0305 m_tabboxActive = std::max(m_tabboxActive - 1, 0); 0306 } 0307 0308 bool SlideBackEffect::isWindowUsable(EffectWindow *w) 0309 { 0310 return w && (w->isNormalWindow() || w->isDialog()) && !w->keepAbove() && !w->isDeleted() && !w->isMinimized(); 0311 } 0312 0313 bool SlideBackEffect::intersects(EffectWindow *windowUnder, const QRect &windowOverGeometry) 0314 { 0315 QRect windowUnderGeometry = getModalGroupGeometry(windowUnder); 0316 return windowUnderGeometry.intersects(windowOverGeometry); 0317 } 0318 0319 QList<EffectWindow *> SlideBackEffect::usableWindows(const QList<EffectWindow *> &allWindows) 0320 { 0321 QList<EffectWindow *> retList; 0322 auto isWindowVisible = [](const EffectWindow *window) { 0323 return window && effects->virtualScreenGeometry().intersects(window->frameGeometry().toAlignedRect()); 0324 }; 0325 for (EffectWindow *tmp : std::as_const(allWindows)) { 0326 if (isWindowUsable(tmp) && isWindowVisible(tmp)) { 0327 retList.append(tmp); 0328 } 0329 } 0330 return retList; 0331 } 0332 0333 QRect SlideBackEffect::getModalGroupGeometry(EffectWindow *w) 0334 { 0335 QRect modalGroupGeometry = w->frameGeometry().toRect(); 0336 if (w->isModal()) { 0337 const auto mainWindows = w->mainWindows(); 0338 for (EffectWindow *modalWindow : mainWindows) { 0339 modalGroupGeometry = modalGroupGeometry.united(getModalGroupGeometry(modalWindow)); 0340 } 0341 } 0342 return modalGroupGeometry; 0343 } 0344 0345 bool SlideBackEffect::isActive() const 0346 { 0347 return motionManager.managingWindows(); 0348 } 0349 0350 } // Namespace 0351 0352 #include "moc_slideback.cpp"