diff --git a/atoms.cpp b/atoms.cpp index 7dd4aece15..6d1dbe99fa 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -129,6 +129,9 @@ Atoms::Atoms() atoms[n] = &kde_first_in_window_list; names[n++] = (char*) "_KDE_FIRST_IN_WINDOWLIST"; + atoms[n] = &kde_skip_close_animation; + names[n++] = (char*) "_KDE_NET_WM_SKIP_CLOSE_ANIMATION"; + assert(n <= max); XInternAtoms(display(), names, n, false, atoms_return); diff --git a/atoms.h b/atoms.h index 95e1bde9ee..07829d966f 100644 --- a/atoms.h +++ b/atoms.h @@ -65,6 +65,7 @@ public: Atom net_wm_opaque_region; Atom kde_net_wm_tab_group; Atom kde_first_in_window_list; + Atom kde_skip_close_animation; }; diff --git a/events.cpp b/events.cpp index 19532b90ff..5392b18c90 100644 --- a/events.cpp +++ b/events.cpp @@ -1557,6 +1557,8 @@ void Toplevel::propertyNotifyEvent(XPropertyEvent* e) getShadow(); else if (e->atom == atoms->net_wm_opaque_region) getWmOpaqueRegion(); + else if (e->atom == atoms->kde_skip_close_animation) + getSkipCloseAnimation(); break; } emit propertyNotify(this, e->atom); diff --git a/libkwineffects/kwineffects.cpp b/libkwineffects/kwineffects.cpp index 927a44e83e..2d53a9d839 100644 --- a/libkwineffects/kwineffects.cpp +++ b/libkwineffects/kwineffects.cpp @@ -710,6 +710,7 @@ WINDOW_HELPER(bool, isDeleted, "deleted") WINDOW_HELPER(bool, hasOwnShape, "shaped") WINDOW_HELPER(QString, windowRole, "windowRole") WINDOW_HELPER(QStringList, activities, "activities") +WINDOW_HELPER(bool, skipsCloseAnimation, "skipsCloseAnimation") QString EffectWindow::windowClass() const { diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h index 9a03eeb335..872482b700 100644 --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -1440,6 +1440,13 @@ class KWIN_EXPORT EffectWindow : public QObject * @since 4.11 **/ Q_PROPERTY(bool visible READ isVisible) + /** + * Whether the window does not want to be animated on window close. + * In case this property is @c true it is not useful to start an animation on window close. + * The window will not be visible, but the animation hooks are executed. + * @since 5.0 + **/ + Q_PROPERTY(bool skipsCloseAnimation READ skipsCloseAnimation) public: /** Flags explaining why painting should be disabled */ enum { @@ -1662,6 +1669,11 @@ public: **/ bool isVisible() const; + /** + * @since 5.0 + **/ + bool skipsCloseAnimation() const; + /** * Can be used to by effects to store arbitrary data in the EffectWindow. */ diff --git a/manage.cpp b/manage.cpp index e1e8ae5409..41a525c871 100644 --- a/manage.cpp +++ b/manage.cpp @@ -129,6 +129,7 @@ bool Client::manage(xcb_window_t w, bool isMapped) getWmNormalHints(); // Get xSizeHint getMotifHints(); getWmOpaqueRegion(); + getSkipCloseAnimation(); // TODO: Try to obey all state information from info->state() diff --git a/scene.cpp b/scene.cpp index 0fc30f85d0..2b1c2218b1 100644 --- a/scene.cpp +++ b/scene.cpp @@ -402,6 +402,10 @@ void Scene::paintWindow(Window* w, int mask, QRegion region, WindowQuadList quad region &= QRect(0, 0, displayWidth(), displayHeight()); if (region.isEmpty()) // completely clipped return; + if (w->window()->isDeleted() && w->window()->skipsCloseAnimation()) { + // should not get painted + return; + } if (s_recursionCheck == w) { return; diff --git a/toplevel.cpp b/toplevel.cpp index cfe9e4b1e1..19ed0bd4b6 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -52,6 +52,7 @@ Toplevel::Toplevel() , unredirectSuspend(false) , m_damageReplyPending(false) , m_screen(0) + , m_skipCloseAnimation(false) { connect(this, SIGNAL(damaged(KWin::Toplevel*,QRect)), SIGNAL(needsRepaint())); connect(screens(), SIGNAL(changed()), SLOT(checkScreen())); @@ -128,6 +129,7 @@ void Toplevel::copyToDeleted(Toplevel* c) window_role = c->windowRole(); opaque_region = c->opaqueRegion(); m_screen = c->m_screen; + m_skipCloseAnimation = c->m_skipCloseAnimation; } // before being deleted, remove references to everything that's now @@ -470,6 +472,34 @@ pid_t Toplevel::pid() const return info->pid(); } +void Toplevel::getSkipCloseAnimation() +{ + xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, window(), atoms->kde_skip_close_animation, XCB_ATOM_CARDINAL, 0, 1); + ScopedCPointer reply(xcb_get_property_reply(connection(), cookie, NULL)); + bool newValue = false; + if (!reply.isNull()) { + if (reply->format == 32 && reply->type == XCB_ATOM_CARDINAL && reply->value_len == 1) { + const uint32_t value = *reinterpret_cast(xcb_get_property_value(reply.data())); + newValue = (value != 0); + } + } + setSkipCloseAnimation(newValue); +} + +bool Toplevel::skipsCloseAnimation() const +{ + return m_skipCloseAnimation; +} + +void Toplevel::setSkipCloseAnimation(bool set) +{ + if (set == m_skipCloseAnimation) { + return; + } + m_skipCloseAnimation = set; + emit skipCloseAnimationChanged(); +} + } // namespace #include "toplevel.moc" diff --git a/toplevel.h b/toplevel.h index 83b1512c0c..c8dcc6b65d 100644 --- a/toplevel.h +++ b/toplevel.h @@ -163,6 +163,12 @@ class Toplevel * Whether the window has an own shape **/ Q_PROPERTY(bool shaped READ shape NOTIFY shapedChanged) + /** + * Whether the window does not want to be animated on window close. + * There are legit reasons for this like a screenshot application which does not want it's + * window being captured. + **/ + Q_PROPERTY(bool skipsCloseAnimation READ skipsCloseAnimation WRITE setSkipCloseAnimation NOTIFY skipCloseAnimationChanged) public: explicit Toplevel(); Window frameId() const; @@ -306,6 +312,9 @@ public: */ void getDamageRegionReply(); + bool skipsCloseAnimation() const; + void setSkipCloseAnimation(bool set); + signals: void opacityChanged(KWin::Toplevel* toplevel, qreal oldOpacity); void damaged(KWin::Toplevel* toplevel, const QRect& damage); @@ -333,6 +342,7 @@ signals: * @since 4.11 **/ void screenChanged(); + void skipCloseAnimationChanged(); protected Q_SLOTS: /** @@ -367,6 +377,7 @@ protected: void getResourceClass(); void getWindowRole(); + void getSkipCloseAnimation(); virtual void debug(QDebug& stream) const = 0; void copyToDeleted(Toplevel* c); void disownDataPassedToDeleted(); @@ -408,6 +419,7 @@ private: QRegion opaque_region; xcb_xfixes_fetch_region_cookie_t m_regionCookie; int m_screen; + bool m_skipCloseAnimation; // when adding new data members, check also copyToDeleted() }; diff --git a/unmanaged.cpp b/unmanaged.cpp index 4583ce076f..f5c761d240 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -80,6 +80,7 @@ bool Unmanaged::track(Window w) XShapeSelectInput(display(), w, ShapeNotifyMask); detectShape(w); getWmOpaqueRegion(); + getSkipCloseAnimation(); setupCompositing(); ungrabXServer(); if (effects)