summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorCabopust2021-02-25 16:17:30 +0300
committerCabopust2021-02-25 16:17:30 +0300
commit423b21ad2e1ae10a4bce2bbd239e36eb60bc41a2 (patch)
treefacedf4bb358bc77cddd22f4ba76e09d0d17b6c1
downloadaur-423b21ad2e1ae10a4bce2bbd239e36eb60bc41a2.tar.gz
Initial push
-rw-r--r--.SRCINFO33
-rw-r--r--.gitignore11
-rw-r--r--PKGBUILD41
-rw-r--r--kwin-unredirect.install8
-rw-r--r--unredirect.patch793
5 files changed, 886 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..2c8a65e47261
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,33 @@
+pkgbase = kwin-unredirect
+ pkgdesc = KWin compositor with redirection patch from Sporif
+ pkgver = 5.21.1
+ pkgrel = 1
+ url = https://kde.org/plasma-desktop/
+ install = kwin-unredirect.install
+ arch = x86_64
+ groups = plasma
+ license = LGPL
+ makedepends = extra-cmake-modules
+ makedepends = qt5-tools
+ makedepends = kdoctools
+ makedepends = krunner
+ depends = kscreenlocker
+ depends = xcb-util-cursor
+ depends = plasma-framework
+ depends = kcmutils
+ depends = kwayland-server
+ depends = breeze
+ depends = qt5-script
+ depends = pipewire
+ depends = libqaccessibilityclient
+ depends = lcms2
+ optdepends = qt5-virtualkeyboard: virtual keyboard support for kwin-wayland
+ provides = kwin
+ conflicts = kwin
+ source = https://download.kde.org/stable/plasma/5.21.1/kwin-5.21.1.tar.xz
+ source = unredirect.patch
+ sha256sums = SKIP
+ sha256sums = 42fcd07ad64c101260878ec756d27189620ac36fddbff83ac6a0c2119ebbd5df
+
+pkgname = kwin-unredirect
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000000..80799818d927
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+.*
+!.gitignore
+!.SRCINFO
+*~
+*.run
+*.pkg
+*.bak
+*.tar.gz
+*.tar.zst
+src/
+pkg/ \ No newline at end of file
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..08f356ac9dca
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,41 @@
+# Maintainer: Vasiliy Stelmachenok <cabopust@yandex.ru>
+# Contributor: Felix Yan <felixonmars@archlinux.org>
+# Contributor: Antonio Rojas <arojas@archlinux.org>
+# Contributor: Andrea Scarpino <andrea@archlinux.org>
+
+pkgname=kwin-unredirect
+pkgver=5.21.1
+pkgrel=1
+pkgdesc='KWin compositor with redirection patch from Sporif'
+arch=(x86_64)
+url='https://kde.org/plasma-desktop/'
+license=(LGPL)
+depends=(kscreenlocker xcb-util-cursor plasma-framework kcmutils kwayland-server breeze
+ qt5-script pipewire libqaccessibilityclient lcms2)
+makedepends=(extra-cmake-modules qt5-tools kdoctools krunner)
+conflicts=(kwin)
+provides=(kwin)
+optdepends=('qt5-virtualkeyboard: virtual keyboard support for kwin-wayland')
+groups=(plasma)
+source=("https://download.kde.org/stable/plasma/$pkgver/kwin-$pkgver.tar.xz"
+ 'unredirect.patch')
+install=$pkgname.install
+sha256sums=('SKIP'
+ '42fcd07ad64c101260878ec756d27189620ac36fddbff83ac6a0c2119ebbd5df')
+
+prepare() {
+ cd "${srcdir}/kwin-$pkgver"
+ # https://gist.github.com/Sporif/7f18d95dd89af8ec386dd1fbbe11bf67
+ patch -Np1 -i "${srcdir}/unredirect.patch"
+}
+
+build() {
+ cmake -B build -S kwin-$pkgver \
+ -DCMAKE_INSTALL_LIBEXECDIR=lib \
+ -DBUILD_TESTING=OFF
+ cmake --build build
+}
+
+package() {
+ DESTDIR="$pkgdir" cmake --install build
+} \ No newline at end of file
diff --git a/kwin-unredirect.install b/kwin-unredirect.install
new file mode 100644
index 000000000000..5d249b2f744e
--- /dev/null
+++ b/kwin-unredirect.install
@@ -0,0 +1,8 @@
+post_install() {
+# this is executed by make install, but doesn't work since setcap can only be run as root
+ setcap CAP_SYS_NICE=+ep /usr/bin/kwin_wayland
+}
+
+post_upgrade() {
+ post_install
+}
diff --git a/unredirect.patch b/unredirect.patch
new file mode 100644
index 000000000000..365e85844ce4
--- /dev/null
+++ b/unredirect.patch
@@ -0,0 +1,793 @@
+diff --git a/composite.cpp b/composite.cpp
+index c529f398e..ce5b0bd08 100644
+--- a/composite.cpp
++++ b/composite.cpp
+@@ -109,11 +109,16 @@ Compositor::Compositor(QObject* workspace)
+ : QObject(workspace)
+ , m_state(State::Off)
+ , m_selectionOwner(nullptr)
++ , forceUnredirectCheck(false)
+ , m_scene(nullptr)
+ {
+ connect(options, &Options::configChanged, this, &Compositor::configChanged);
+ connect(options, &Options::animationSpeedChanged, this, &Compositor::configChanged);
+
++ connect(&unredirectTimer, &QTimer::timeout, this, &Compositor::delayedCheckUnredirect);
++ connect(options, &Options::unredirectFullscreenChanged, this, &Compositor::delayedCheckUnredirect);
++ unredirectTimer.setSingleShot(true);
++
+ // 2 sec which should be enough to restart the compositor.
+ static const int compositorLostMessageDelay = 2000;
+
+@@ -705,6 +710,54 @@ bool Compositor::isActive()
+ return m_state == State::On;
+ }
+
++void Compositor::checkUnredirect()
++{
++ checkUnredirect(false);
++}
++
++// force is needed when the list of windows changes (e.g. a window goes away)
++void Compositor::checkUnredirect(bool force)
++{
++ if (!isActive() || !m_scene->overlayWindow() || m_scene->overlayWindow()->window() == 0 || !options->isUnredirectFullscreen())
++ return;
++ if (force)
++ forceUnredirectCheck = true;
++ if (!unredirectTimer.isActive())
++ unredirectTimer.start(0);
++}
++
++void Compositor::delayedCheckUnredirect()
++{
++ if (!isActive() || !m_scene->overlayWindow() || m_scene->overlayWindow()->window() == 0 || !(options->isUnredirectFullscreen() || sender() == options))
++ return;
++ QList<Toplevel*> list;
++ bool changed = forceUnredirectCheck;
++ foreach (X11Client * c, Workspace::self()->clientList())
++ list.append(c);
++ foreach (Unmanaged * c, Workspace::self()->unmanagedList())
++ list.append(c);
++ foreach (Toplevel * c, list) {
++ if (c->updateUnredirectedState()) {
++ changed = true;
++ break;
++ }
++ }
++ // no desktops, no Deleted ones
++ if (!changed)
++ return;
++ forceUnredirectCheck = false;
++ // Cut out parts from the overlay window where unredirected windows are,
++ // so that they are actually visible.
++ const QSize &s = screens()->size();
++ QRegion reg(0, 0, s.width(), s.height());
++ foreach (Toplevel * c, list) {
++ if (c->unredirected())
++ reg -= c->frameGeometry();
++ }
++ m_scene->overlayWindow()->setShape(reg);
++ addRepaint(reg);
++}
++
+ WaylandCompositor::WaylandCompositor(QObject *parent)
+ : Compositor(parent)
+ {
+diff --git a/composite.h b/composite.h
+index a904fd114..d15632156 100644
+--- a/composite.h
++++ b/composite.h
+@@ -50,6 +50,13 @@ public:
+ */
+ void scheduleRepaint();
+
++ /**
++ * Checks for possibly unredirectable windows.
++ */
++ void checkUnredirect();
++ void checkUnredirect(bool force);
++ void delayedCheckUnredirect();
++
+ /**
+ * Toggles compositing, that is if the Compositor is suspended it will be resumed
+ * and if the Compositor is active it will be suspended.
+@@ -138,6 +145,8 @@ private:
+ QTimer m_releaseSelectionTimer;
+ QList<xcb_atom_t> m_unusedSupportProperties;
+ QTimer m_unusedSupportPropertyTimer;
++ QTimer unredirectTimer;
++ bool forceUnredirectCheck;
+ Scene *m_scene;
+ int m_framesToTestForSafety = 3;
+ QMap<RenderLoop *, AbstractOutput *> m_renderLoops;
+diff --git a/deleted.cpp b/deleted.cpp
+index a93fa8da5..13cdbc633 100644
+--- a/deleted.cpp
++++ b/deleted.cpp
+@@ -297,5 +297,10 @@ void Deleted::removeTransientFor(Deleted *parent)
+ m_transientFor.removeAll(parent);
+ }
+
++bool Deleted::shouldUnredirect() const
++{
++ return false;
++}
++
+ } // namespace
+
+diff --git a/deleted.h b/deleted.h
+index 3d545894c..a99aa5b33 100644
+--- a/deleted.h
++++ b/deleted.h
+@@ -168,6 +168,8 @@ public:
+ bool isOutline() const override {
+ return m_wasOutline;
+ }
++protected:
++ bool shouldUnredirect() const override;
+
+ private Q_SLOTS:
+ void mainClientClosed(KWin::Toplevel *client);
+diff --git a/effects.cpp b/effects.cpp
+index 13a30abc0..c862f668e 100644
+--- a/effects.cpp
++++ b/effects.cpp
+@@ -627,6 +627,7 @@ void EffectsHandlerImpl::setActiveFullScreenEffect(Effect* e)
+ }
+ const bool activeChanged = (e == nullptr || fullscreen_effect == nullptr);
+ fullscreen_effect = e;
++ m_compositor->checkUnredirect();
+ emit activeFullScreenEffectChanged();
+ if (activeChanged) {
+ emit hasActiveFullScreenEffectChanged();
+diff --git a/inputpanelv1client.h b/inputpanelv1client.h
+index 2491c46d5..25c6418bd 100644
+--- a/inputpanelv1client.h
++++ b/inputpanelv1client.h
+@@ -35,6 +35,7 @@ public:
+ bool isResizable() const override { return false; }
+ bool isMovable() const override { return false; }
+ bool isMovableAcrossScreens() const override { return false; }
++ bool shouldUnredirect() const override { return false; }
+ bool acceptsFocus() const override { return false; }
+ void closeWindow() override {}
+ bool takeFocus() override { return false; }
+diff --git a/internal_client.cpp b/internal_client.cpp
+index a01da4b52..55a72396b 100644
+--- a/internal_client.cpp
++++ b/internal_client.cpp
+@@ -572,4 +572,9 @@ void InternalClient::updateInternalWindowGeometry()
+ commitGeometry(clientRectToFrameRect(m_internalWindow->geometry()));
+ }
+
++bool InternalClient::shouldUnredirect() const
++{
++ return false;
++}
++
+ }
+diff --git a/internal_client.h b/internal_client.h
+index 187c78f32..13097a299 100644
+--- a/internal_client.h
++++ b/internal_client.h
+@@ -33,6 +33,7 @@ public:
+ QPoint clientContentPos() const override;
+ QSize minSize() const override;
+ QSize maxSize() const override;
++ bool shouldUnredirect() const override;
+ QRect transparentRect() const override;
+ NET::WindowType windowType(bool direct = false, int supported_types = 0) const override;
+ double opacity() const override;
+diff --git a/kcmkwin/kwincompositing/compositing.ui b/kcmkwin/kwincompositing/compositing.ui
+index 398043fa0..24e20f2a3 100644
+--- a/kcmkwin/kwincompositing/compositing.ui
++++ b/kcmkwin/kwincompositing/compositing.ui
+@@ -323,6 +323,18 @@ Alternatively, you might want to use the XRender backend instead.</string>
+ </item>
+ </widget>
+ </item>
++ <item row="14" column="1">
++ <widget class="QCheckBox" name="kcfg_unredirectFullscreen">
++ <property name="toolTip">
++ <string>Applications do not have to set any hint to suspend compositing when the window is full-screen.
++ This delivers lower latency and has no delay when compared to the other option.
++ Some people have reported crashes under Intel, but I am not sure if this is the case.</string>
++ </property>
++ <property name="text">
++ <string>Suspend compositor for full-screen windows</string>
++ </property>
++ </widget>
++ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+diff --git a/kcmkwin/kwincompositing/kwincompositing_setting.kcfg b/kcmkwin/kwincompositing/kwincompositing_setting.kcfg
+index 7278d09b7..d2fa506f3 100644
+--- a/kcmkwin/kwincompositing/kwincompositing_setting.kcfg
++++ b/kcmkwin/kwincompositing/kwincompositing_setting.kcfg
+@@ -79,6 +79,10 @@
+ <default>LatencyMedium</default>
+ </entry>
+
++ <entry name="unredirectFullscreen" key="UnredirectFullscreen" type="Bool">
++ <default>true</default>
++ </entry>
++
+ </group>
+
+ </kcfg>
+diff --git a/kcmkwin/kwinrules/rulesmodel.cpp b/kcmkwin/kwinrules/rulesmodel.cpp
+index a3ad8ade0..aaf04ee16 100644
+--- a/kcmkwin/kwinrules/rulesmodel.cpp
++++ b/kcmkwin/kwinrules/rulesmodel.cpp
+@@ -639,6 +639,11 @@ void RulesModel::populateRuleList()
+ RulePolicy::ForceRule, RuleItem::Boolean,
+ i18n("Block compositing"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("composite-track-on")));
++
++ addRule(new RuleItem(QLatin1String("allowunredirect"),
++ RulePolicy::ForceRule, RuleItem::Boolean,
++ i18n("Allow unredirect"), i18n("Appearance & Fixes"),
++ QIcon::fromTheme("composite-track-on")));
+ }
+
+
+diff --git a/kwin.kcfg b/kwin.kcfg
+index 7485efc7f..af496d6d2 100644
+--- a/kwin.kcfg
++++ b/kwin.kcfg
+@@ -239,6 +239,9 @@
+ <min>4</min>
+ <max>6</max>
+ </entry>
++ <entry name="UnredirectFullscreen" type="Bool">
++ <default>false</default>
++ </entry>
+ <entry name="GLPlatformInterface" type="String">
+ <default>glx</default>
+ </entry>
+diff --git a/layers.cpp b/layers.cpp
+index a82fed2b8..bf384ad2e 100644
+--- a/layers.cpp
++++ b/layers.cpp
+@@ -715,6 +715,9 @@ QList<Toplevel *> Workspace::xStackingOrder() const
+ if (m_xStackingDirty) {
+ const_cast<Workspace*>(this)->updateXStackingOrder();
+ }
++ if (m_compositor) {
++ const_cast<Workspace*>(this)->m_compositor->checkUnredirect();
++ }
+ return x_stacking;
+ }
+
+diff --git a/layershellv1client.h b/layershellv1client.h
+index 7c3e0cceb..8769a4145 100644
+--- a/layershellv1client.h
++++ b/layershellv1client.h
+@@ -47,6 +47,7 @@ public:
+ protected:
+ Layer belongsToLayer() const override;
+ bool acceptsFocus() const override;
++ bool shouldUnredirect() const override { return false; }
+ void requestGeometry(const QRect &rect) override;
+
+ private:
+diff --git a/options.cpp b/options.cpp
+index 52030f142..fd695c741 100644
+--- a/options.cpp
++++ b/options.cpp
+@@ -58,6 +58,7 @@ Options::Options(QObject *parent)
+ , m_compositingMode(Options::defaultCompositingMode())
+ , m_useCompositing(Options::defaultUseCompositing())
+ , m_hiddenPreviews(Options::defaultHiddenPreviews())
++ , m_unredirectFullscreen(Options::defaultUnredirectFullscreen())
+ , m_glSmoothScale(Options::defaultGlSmoothScale())
+ , m_xrenderSmoothScale(Options::defaultXrenderSmoothScale())
+ , m_glStrictBinding(Options::defaultGlStrictBinding())
+@@ -554,6 +555,20 @@ void Options::setHiddenPreviews(int hiddenPreviews)
+ emit hiddenPreviewsChanged();
+ }
+
++void Options::setUnredirectFullscreen(bool unredirectFullscreen)
++{
++ //if (GLPlatform::instance()->driver() == Driver_Intel)
++ //unredirectFullscreen = false; // bug #252817
++ if (m_unredirectFullscreen == unredirectFullscreen) {
++ return;
++ }
++ //if (GLPlatform::instance()->driver() == Driver_Intel) { // write back the value
++ //KConfigGroup(m_settings->config(), "Compositing").writeEntry("UnredirectFullscreen", false);
++ //}
++ m_unredirectFullscreen = unredirectFullscreen;
++ emit unredirectFullscreenChanged();
++}
++
+ void Options::setGlSmoothScale(int glSmoothScale)
+ {
+ if (m_glSmoothScale == glSmoothScale) {
+@@ -911,6 +926,8 @@ void Options::reloadCompositingSettings(bool force)
+ else if (hps == 6)
+ previews = HiddenPreviewsAlways;
+ setHiddenPreviews(previews);
++
++ setUnredirectFullscreen(config.readEntry("UnredirectFullscreen",Options::defaultUnredirectFullscreen()));
+
+ auto interfaceToKey = [](OpenGLPlatformInterface interface) {
+ switch (interface) {
+@@ -1062,4 +1079,9 @@ bool Options::isUseCompositing() const
+ return m_useCompositing || kwinApp()->platform()->requiresCompositing();
+ }
+
++bool Options::isUnredirectFullscreen() const
++{
++ return m_unredirectFullscreen && !kwinApp()->platform()->requiresCompositing();
++}
++
+ } // namespace
+diff --git a/options.h b/options.h
+index e692a676e..262314259 100644
+--- a/options.h
++++ b/options.h
+@@ -179,6 +179,7 @@ class KWIN_EXPORT Options : public QObject
+ Q_PROPERTY(int compositingMode READ compositingMode WRITE setCompositingMode NOTIFY compositingModeChanged)
+ Q_PROPERTY(bool useCompositing READ isUseCompositing WRITE setUseCompositing NOTIFY useCompositingChanged)
+ Q_PROPERTY(int hiddenPreviews READ hiddenPreviews WRITE setHiddenPreviews NOTIFY hiddenPreviewsChanged)
++ Q_PROPERTY(bool unredirectFullscreen READ isUnredirectFullscreen WRITE setUnredirectFullscreen NOTIFY unredirectFullscreenChanged)
+ /**
+ * 0 = no, 1 = yes when transformed,
+ * 2 = try trilinear when transformed; else 1,
+@@ -564,6 +565,7 @@ public:
+ HiddenPreviews hiddenPreviews() const {
+ return m_hiddenPreviews;
+ }
++ bool isUnredirectFullscreen() const;
+ // OpenGL
+ // 0 = no, 1 = yes when transformed,
+ // 2 = try trilinear when transformed; else 1,
+@@ -657,6 +659,7 @@ public:
+ void setCompositingMode(int compositingMode);
+ void setUseCompositing(bool useCompositing);
+ void setHiddenPreviews(int hiddenPreviews);
++ void setUnredirectFullscreen(bool unredirectFullscreen);
+ void setGlSmoothScale(int glSmoothScale);
+ void setXrenderSmoothScale(bool xrenderSmoothScale);
+ void setGlStrictBinding(bool glStrictBinding);
+@@ -739,6 +742,9 @@ public:
+ static HiddenPreviews defaultHiddenPreviews() {
+ return HiddenPreviewsShown;
+ }
++ static bool defaultUnredirectFullscreen() {
++ return true;
++ }
+ static int defaultGlSmoothScale() {
+ return 2;
+ }
+@@ -833,6 +839,7 @@ Q_SIGNALS:
+ void compositingModeChanged();
+ void useCompositingChanged();
+ void hiddenPreviewsChanged();
++ void unredirectFullscreenChanged();
+ void glSmoothScaleChanged();
+ void xrenderSmoothScaleChanged();
+ void glStrictBindingChanged();
+@@ -878,6 +885,7 @@ private:
+ CompositingType m_compositingMode;
+ bool m_useCompositing;
+ HiddenPreviews m_hiddenPreviews;
++ bool m_unredirectFullscreen;
+ int m_glSmoothScale;
+ bool m_xrenderSmoothScale;
+ // Settings that should be auto-detected
+diff --git a/rules.cpp b/rules.cpp
+index a75b7775d..97dddfa71 100644
+--- a/rules.cpp
++++ b/rules.cpp
+@@ -64,6 +64,7 @@ Rules::Rules()
+ , noborderrule(UnusedSetRule)
+ , decocolorrule(UnusedForceRule)
+ , blockcompositingrule(UnusedForceRule)
++ , allowunredirectrule(UnusedForceRule)
+ , fsplevelrule(UnusedForceRule)
+ , fpplevelrule(UnusedForceRule)
+ , acceptfocusrule(UnusedForceRule)
+@@ -162,6 +163,7 @@ void Rules::readFromSettings(const RuleSettings *settings)
+ decocolorrule = UnusedForceRule;
+
+ READ_FORCE_RULE(blockcompositing,);
++ READ_FORCE_RULE(allowunredirect,);
+ READ_FORCE_RULE(fsplevel,);
+ READ_FORCE_RULE(fpplevel,);
+ READ_FORCE_RULE(acceptfocus,);
+@@ -243,6 +245,7 @@ void Rules::write(RuleSettings *settings) const
+ };
+ WRITE_FORCE_RULE(decocolor, Decocolor, colorToString);
+ WRITE_FORCE_RULE(blockcompositing, Blockcompositing,);
++ WRITE_FORCE_RULE(allowunredirect, Allowunredirect,);
+ WRITE_FORCE_RULE(fsplevel, Fsplevel,);
+ WRITE_FORCE_RULE(fpplevel, Fpplevel,);
+ WRITE_FORCE_RULE(acceptfocus, Acceptfocus,);
+@@ -288,6 +291,7 @@ bool Rules::isEmpty() const
+ && noborderrule == UnusedSetRule
+ && decocolorrule == UnusedForceRule
+ && blockcompositingrule == UnusedForceRule
++ && allowunredirectrule == UnusedForceRule
+ && fsplevelrule == UnusedForceRule
+ && fpplevelrule == UnusedForceRule
+ && acceptfocusrule == UnusedForceRule
+@@ -602,6 +606,7 @@ APPLY_RULE(fullscreen, FullScreen, bool)
+ APPLY_RULE(noborder, NoBorder, bool)
+ APPLY_FORCE_RULE(decocolor, DecoColor, QString)
+ APPLY_FORCE_RULE(blockcompositing, BlockCompositing, bool)
++APPLY_FORCE_RULE(allowunredirect, AllowUnredirect, bool)
+ APPLY_FORCE_RULE(fsplevel, FSP, int)
+ APPLY_FORCE_RULE(fpplevel, FPP, int)
+ APPLY_FORCE_RULE(acceptfocus, AcceptFocus, bool)
+@@ -677,6 +682,7 @@ bool Rules::discardUsed(bool withdrawn)
+ DISCARD_USED_SET_RULE(noborder);
+ DISCARD_USED_FORCE_RULE(decocolor);
+ DISCARD_USED_FORCE_RULE(blockcompositing);
++ DISCARD_USED_FORCE_RULE(allowunredirect);
+ DISCARD_USED_FORCE_RULE(fsplevel);
+ DISCARD_USED_FORCE_RULE(fpplevel);
+ DISCARD_USED_FORCE_RULE(acceptfocus);
+@@ -814,6 +820,7 @@ CHECK_RULE(FullScreen, bool)
+ CHECK_RULE(NoBorder, bool)
+ CHECK_FORCE_RULE(DecoColor, QString)
+ CHECK_FORCE_RULE(BlockCompositing, bool)
++CHECK_FORCE_RULE(AllowUnredirect, bool)
+ CHECK_FORCE_RULE(FSP, int)
+ CHECK_FORCE_RULE(FPP, int)
+ CHECK_FORCE_RULE(AcceptFocus, bool)
+diff --git a/rules.h b/rules.h
+index 7ffa0789f..b63a6ca75 100644
+--- a/rules.h
++++ b/rules.h
+@@ -67,6 +67,7 @@ public:
+ bool checkNoBorder(bool noborder, bool init = false) const;
+ QString checkDecoColor(QString schemeFile) const;
+ bool checkBlockCompositing(bool block) const;
++ bool checkAllowUnredirect(bool allow) const;
+ int checkFSP(int fsp) const;
+ int checkFPP(int fpp) const;
+ bool checkAcceptFocus(bool focus) const;
+@@ -162,6 +163,7 @@ public:
+ bool applyNoBorder(bool& noborder, bool init) const;
+ bool applyDecoColor(QString &schemeFile) const;
+ bool applyBlockCompositing(bool& block) const;
++ bool applyAllowUnredirect(bool& allow) const;
+ bool applyFSP(int& fsp) const;
+ bool applyFPP(int& fpp) const;
+ bool applyAcceptFocus(bool& focus) const;
+@@ -251,6 +253,8 @@ private:
+ ForceRule decocolorrule;
+ bool blockcompositing;
+ ForceRule blockcompositingrule;
++ bool allowunredirect;
++ ForceRule allowunredirectrule;
+ int fsplevel;
+ int fpplevel;
+ ForceRule fsplevelrule;
+diff --git a/rulesettings.kcfg b/rulesettings.kcfg
+index 0cb10da7e..02cab1487 100644
+--- a/rulesettings.kcfg
++++ b/rulesettings.kcfg
+@@ -328,6 +328,15 @@
+ <default code="true">Rules::UnusedForceRule</default>
+ </entry>
+
++ <entry name="allowunredirect" type="Bool">
++ <label>Full-screen unredirection</label>
++ <default>false</default>
++ </entry>
++ <entry name="allowunredirectrule" type="Int">
++ <label>Full-screen unredirection rule type</label>
++ <default code="true">Rules::UnusedForceRule</default>
++ </entry>
++
+ <entry name="fsplevel" type="Int">
+ <label>Focus stealing prevention</label>
+ <default>0</default>
+diff --git a/toplevel.cpp b/toplevel.cpp
+index b85b6e689..836cc98cc 100644
+--- a/toplevel.cpp
++++ b/toplevel.cpp
+@@ -25,6 +25,8 @@
+
+ #include <QDebug>
+
++#include <unistd.h>
++
+ namespace KWin
+ {
+
+@@ -291,6 +293,7 @@ bool Toplevel::setupCompositing()
+ effect_window = new EffectWindowImpl(this);
+
+ Compositor::self()->scene()->addToplevel(this);
++ Compositor::self()->checkUnredirect(true);
+
+ return true;
+ }
+@@ -299,6 +302,7 @@ void Toplevel::finishCompositing(ReleaseReason releaseReason)
+ {
+ if (kwinApp()->operationMode() == Application::OperationModeX11 && damage_handle == XCB_NONE)
+ return;
++ Compositor::self()->checkUnredirect(true);
+ if (effect_window->window() == this) { // otherwise it's already passed to Deleted, don't free data
+ discardWindowPixmap();
+ delete effect_window;
+@@ -827,5 +831,45 @@ QMargins Toplevel::frameMargins() const
+ return QMargins();
+ }
+
++bool Toplevel::updateUnredirectedState()
++{
++ assert(compositing());
++ bool should = options->isUnredirectFullscreen() && shouldUnredirect() && !unredirectSuspend &&
++ !shape() && !hasAlpha() && opacity() == 1.0 &&
++ !static_cast<EffectsHandlerImpl*>(effects)->activeFullScreenEffect();
++ if (should) {
++ if (Screens::self()->count()<2) {
++ usleep(50000);
++ }
++ }
++ if (should == unredirect)
++ return false;
++ static QElapsedTimer lastUnredirect;
++ static const qint64 msecRedirectInterval = 100;
++ if (!lastUnredirect.hasExpired(msecRedirectInterval)) {
++ QTimer::singleShot(msecRedirectInterval, Compositor::self(), static_cast<void (Compositor::*)()>(&Compositor::checkUnredirect));
++ return false;
++ }
++ lastUnredirect.start();
++ unredirect = should;
++ if (unredirect) {
++ qCDebug(KWIN_CORE) << "Unredirecting:" << this;
++ xcb_composite_unredirect_window(connection(), frameId(), XCB_COMPOSITE_REDIRECT_MANUAL);
++ } else {
++ qCDebug(KWIN_CORE) << "Redirecting:" << this;
++ xcb_composite_redirect_window(connection(), frameId(), XCB_COMPOSITE_REDIRECT_MANUAL);
++ discardWindowPixmap();
++ }
++ return true;
++}
++
++void Toplevel::suspendUnredirect(bool suspend)
++{
++ if (unredirectSuspend == suspend)
++ return;
++ unredirectSuspend = suspend;
++ Compositor::self()->checkUnredirect();
++}
++
+ } // namespace
+
+diff --git a/toplevel.h b/toplevel.h
+index 572bef0e7..e5a52a712 100644
+--- a/toplevel.h
++++ b/toplevel.h
+@@ -454,6 +454,9 @@ public:
+ bool hasAlpha() const;
+ virtual bool setupCompositing();
+ virtual void finishCompositing(ReleaseReason releaseReason = ReleaseReason::Release);
++ bool updateUnredirectedState();
++ bool unredirected() const;
++ void suspendUnredirect(bool suspend);
+ Q_INVOKABLE void addRepaint(const QRect& r);
+ Q_INVOKABLE void addRepaint(const QRegion& r);
+ Q_INVOKABLE void addRepaint(int x, int y, int w, int h);
+@@ -719,6 +722,7 @@ protected:
+ void copyToDeleted(Toplevel* c);
+ void disownDataPassedToDeleted();
+ void deleteEffectWindow();
++ virtual bool shouldUnredirect() const = 0;
+ void setDepth(int depth);
+ QRect m_frameGeometry;
+ QRect m_clientGeometry;
+@@ -747,6 +751,8 @@ private:
+ QByteArray resource_class;
+ ClientMachine *m_clientMachine;
+ xcb_window_t m_wmClientLeader;
++ bool unredirect;
++ bool unredirectSuspend; // when unredirected, but pixmap is needed temporarily
+ bool m_damageReplyPending;
+ QRegion opaque_region;
+ xcb_xfixes_fetch_region_cookie_t m_regionCookie;
+@@ -1005,6 +1011,11 @@ inline QByteArray Toplevel::resourceClass() const
+ return resource_class; // it is always lowercase
+ }
+
++inline bool Toplevel::unredirected() const
++{
++ return unredirect;
++}
++
+ inline const ClientMachine *Toplevel::clientMachine() const
+ {
+ return m_clientMachine;
+diff --git a/unmanaged.cpp b/unmanaged.cpp
+index 3e9a0e0c7..a12e5d068 100644
+--- a/unmanaged.cpp
++++ b/unmanaged.cpp
+@@ -189,5 +189,35 @@ bool Unmanaged::setupCompositing()
+ return true;
+ }
+
++bool Unmanaged::shouldUnredirect() const
++{
++ // the pixmap is needed for the login effect, a nicer solution would be the login effect increasing
++ // refcount for the window pixmap (which would prevent unredirect), avoiding this hack
++ if (resourceClass() == "ksplashx"
++ || resourceClass() == "ksplashsimple"
++ || resourceClass() == "ksplashqml"
++ )
++ return false;
++ // it must cover whole display or one xinerama screen, and be the topmost there
++ const int desktop = VirtualDesktopManager::self()->current();
++ // TODO: this mess o-o
++ if (frameGeometry() == workspace()->clientArea(FullArea, frameGeometry().center(), desktop)
++ || frameGeometry() == workspace()->clientArea(ScreenArea, frameGeometry().center(), desktop)) {
++ QList<Toplevel*> stacking = workspace()->xStackingOrder();
++ for (int pos = stacking.count() - 1;
++ pos >= 0;
++ --pos) {
++ Toplevel* c = stacking.at(pos);
++ if (c == this) // is not covered by any other window, ok to unredirect
++ return true;
++ // TODO: check if this works
++ if (c->frameGeometry().intersects(bufferGeometry()))
++ return false;
++ }
++ abort();
++ }
++ return false;
++}
++
+ } // namespace
+
+diff --git a/unmanaged.h b/unmanaged.h
+index bb8d2a20b..0716cef9a 100644
+--- a/unmanaged.h
++++ b/unmanaged.h
+@@ -43,6 +43,9 @@ public:
+ public Q_SLOTS:
+ void release(ReleaseReason releaseReason = ReleaseReason::Release);
+
++ protected:
++ bool shouldUnredirect() const override;
++
+ private:
+ ~Unmanaged() override; // use release()
+ // handlers for X11 events
+diff --git a/workspace.cpp b/workspace.cpp
+index d701525b6..d95cb8b58 100644
+--- a/workspace.cpp
++++ b/workspace.cpp
+@@ -552,6 +552,11 @@ X11Client *Workspace::createClient(xcb_window_t w, bool is_mapped)
+ connect(c, &X11Client::blockingCompositingChanged, compositor, &X11Compositor::updateClientCompositeBlocking);
+ }
+ connect(c, &X11Client::clientFullScreenSet, ScreenEdges::self(), &ScreenEdges::checkBlocking);
++ connect(c, &X11Client::activeChanged, m_compositor, static_cast<void (Compositor::*)()>(&Compositor::checkUnredirect));
++ // THIS ONE PLEASE
++ connect(c, &X11Client::fullScreenChanged, m_compositor, static_cast<void (Compositor::*)()>(&Compositor::checkUnredirect));
++ connect(c, &X11Client::geometryChanged, m_compositor, static_cast<void (Compositor::*)()>(&Compositor::checkUnredirect));
++ connect(c, &X11Client::geometryShapeChanged, m_compositor, static_cast<void (Compositor::*)()>(&Compositor::checkUnredirect));
+ if (!c->manage(w, is_mapped)) {
+ X11Client::deleteClient(c);
+ return nullptr;
+@@ -1985,6 +1990,9 @@ void Workspace::desktopResized()
+ if (effects) {
+ static_cast<EffectsHandlerImpl*>(effects)->desktopResized(geom.size());
+ }
++
++ // best guess.
++ m_compositor->checkUnredirect(true);
+ }
+
+ void Workspace::saveOldScreenSizes()
+diff --git a/x11client.cpp b/x11client.cpp
+index 960fc24c4..68f72d5a2 100644
+--- a/x11client.cpp
++++ b/x11client.cpp
+@@ -1596,6 +1596,9 @@ void X11Client::internalShow()
+ m_decoInputExtent.map();
+ updateHiddenPreview();
+ }
++ if (Compositor::self()->isActive()) {
++ Compositor::self()->checkUnredirect();
++ }
+ emit windowShown(this);
+ }
+
+@@ -1611,6 +1614,9 @@ void X11Client::internalHide()
+ updateHiddenPreview();
+ addWorkspaceRepaint(visibleRect());
+ workspace()->clientHidden(this);
++ if (Compositor::self()->isActive()) {
++ Compositor::self()->checkUnredirect();
++ }
+ emit windowHidden(this);
+ }
+
+@@ -1629,6 +1635,9 @@ void X11Client::internalKeep()
+ updateHiddenPreview();
+ addWorkspaceRepaint(visibleRect());
+ workspace()->clientHidden(this);
++ if (Compositor::self()->isActive()) {
++ Compositor::self()->checkUnredirect();
++ }
+ }
+
+ /**
+@@ -2896,6 +2905,10 @@ void X11Client::move(int x, int y, ForceGeometry_t force)
+ updateGeometryBeforeUpdateBlocking();
+ screens()->setCurrent(this);
+ workspace()->updateStackingOrder();
++ if (Compositor::self()->isActive()) {
++ // TODO: there was a todo here but I don't know
++ Compositor::self()->checkUnredirect();
++ }
+ // client itself is not damaged
+ if (oldBufferGeometry != bufferGeometry()) {
+ emit bufferGeometryChanged(this, oldBufferGeometry);
+@@ -4920,4 +4933,35 @@ void X11Client::updateWindowPixmap()
+ }
+ }
+
++// TODO THIS
++bool X11Client::shouldUnredirect() const
++{
++ if (isActiveFullScreen()) {
++ if (!rules()->checkAllowUnredirect(true)) return false;
++ QList<Toplevel*> stacking = workspace()->xStackingOrder();
++ for (int pos = stacking.count() - 1;
++ pos >= 0;
++ --pos) {
++ Toplevel* c = stacking.at(pos);
++ if (c == this) { // is not covered by any other window, ok to unredirect
++ //printf("yes.\n");
++ return true;
++ }
++ if (c->frameGeometry().intersects(frameGeometry())) {
++ // check whether this is an invisible floating icon at the top left corner
++ if (c->frameGeometry()==QRect(0,0,32,32)) {
++ //printf("yes via hack.\n");
++ return true;
++ }
++ //printf("no. this: %d %d %d %d. other: %d %d %d %d.\n",geometry().x(),geometry().y(),geometry().width(),geometry().height(),c->geometry().x(),c->geometry().y(),c->geometry().width(),c->geometry().height());
++ return false;
++ }
++ }
++ //printf("ABORT\n");
++ abort();
++ }
++ return false;
++}
++
++
+ } // namespace
+diff --git a/x11client.h b/x11client.h
+index d3081bba5..9f62326e5 100644
+--- a/x11client.h
++++ b/x11client.h
+@@ -317,6 +317,7 @@ private:
+ bool motionNotifyEvent(xcb_window_t w, int state, int x, int y, int x_root, int y_root);
+
+ protected:
++ bool shouldUnredirect() const override;
+ void addDamage(const QRegion &damage) override;
+ bool belongsToSameApplication(const AbstractClient *other, SameApplicationChecks checks) const override;
+ void doSetActive() override;
+diff --git a/xdgshellclient.h b/xdgshellclient.h
+index 7d783fdf6..d0cf9f33a 100644
+--- a/xdgshellclient.h
++++ b/xdgshellclient.h
+@@ -63,6 +63,7 @@ public:
+ virtual void installPlasmaShellSurface(KWaylandServer::PlasmaShellSurfaceInterface *shellSurface) = 0;
+
+ protected:
++ bool shouldUnredirect() const override { return false; }
+ void requestGeometry(const QRect &rect) override;
+
+ virtual XdgSurfaceConfigure *sendRoleConfigure() const = 0;