From 71b7698c80557e19f432d8cfbce3e8831a9705f9 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Sun, 24 Nov 2024 15:09:47 +0100 Subject: [PATCH 01/19] windowrules: implement --- README.md | 2 +- docs/configuration.md | 147 ++++++-- flake.lock | 6 +- src/CMakeLists.txt | 9 +- src/blur.cpp | 435 ++++++------------------ src/blur.h | 68 ++-- src/blurconfig.kcfgc | 5 - src/blurwindow.cpp | 256 ++++++++++++++ src/blurwindow.h | 83 +++++ src/{blur.kcfg => config/config.kcfg} | 5 +- src/config/config.kcfgc | 5 + src/kcm/CMakeLists.txt | 2 +- src/kcm/blur_config.cpp | 9 +- src/kcm/blur_config.ui | 56 ++- src/settings.cpp | 40 --- src/settings.h | 69 ---- src/utils.h | 7 +- src/windowrules/windowproperties.cpp | 46 +++ src/windowrules/windowproperties.h | 86 +++++ src/windowrules/windowrule.cpp | 21 ++ src/windowrules/windowrule.h | 49 +++ src/windowrules/windowrulecondition.cpp | 107 ++++++ src/windowrules/windowrulecondition.h | 105 ++++++ src/windowrules/windowrulelist.cpp | 224 ++++++++++++ src/windowrules/windowrulelist.h | 32 ++ 25 files changed, 1333 insertions(+), 541 deletions(-) delete mode 100644 src/blurconfig.kcfgc create mode 100644 src/blurwindow.cpp create mode 100644 src/blurwindow.h rename src/{blur.kcfg => config/config.kcfg} (95%) create mode 100644 src/config/config.kcfgc delete mode 100644 src/settings.cpp delete mode 100644 src/settings.h create mode 100644 src/windowrules/windowproperties.cpp create mode 100644 src/windowrules/windowproperties.h create mode 100644 src/windowrules/windowrule.cpp create mode 100644 src/windowrules/windowrule.h create mode 100644 src/windowrules/windowrulecondition.cpp create mode 100644 src/windowrules/windowrulecondition.h create mode 100644 src/windowrules/windowrulelist.cpp create mode 100644 src/windowrules/windowrulelist.h diff --git a/README.md b/README.md index bad8506d67..136ce4fd0a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Better Blur (formerly kwin-effects-forceblur) is a fork the KWin Blur effect for - X11 and Wayland support - Force blur - Rounded corners with optional anti-aliasing -- Optional blur texture caching for much lower GPU usage, works best with tiling +- Static blur for much lower GPU usage, works best with tiling ### Bug fixes Fixes for blur-related Plasma bugs that haven't been patched yet. diff --git a/docs/configuration.md b/docs/configuration.md index 98126a8eeb..2362ef5ae1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,42 +1,131 @@ -# General -### Window opacity affects blur -Since Plasma 6, window opacity now affects blur opacity with no option to disable it in the stock blur effect. +# Simple +A simple configuration system with a UI. For more complex configuration (for example blur menus but not firefox's menus), use window rules. -Enabled (default): -![image](https://github.com/taj-ny/kwin-effects-forceblur/assets/79316397/525a3611-62f0-4c7e-b01c-253a05cbd3ca) +Obvious options are not explained here. -Disabled: -![image](https://github.com/taj-ny/kwin-effects-forceblur/assets/79316397/b4f35a24-e288-4c51-9707-494942abdaa0) +## General -# Force blur -### Blur window decorations -Whether to blur window decorations, including borders. Enable this if your window decoration doesn't support blur, or you want rounded top corners. +| Option | Description | +|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Noise strength** | Noise is used to mask color banding. | +| **Window opacity affects blur** | Whether the blur opacity should be affected by the window's opacity. Window fading animations will still affect the blur opacity.

Enabled:

Disabled:
| -This option will override the blur region specified by the decoration. +## Force blur +| Option | Description | +|-------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Window classes** | A list of classes of windows to force blur, separated by new line.

Regular expressions are supported and can be specified by using the ``$regex:`` prefix. Example: ``$regex:org\.kde\..*`` | +| **Blur window decorations as well** | Whether to blur the window decorations (titlebar and borders) in addition to the content. This will override the decoration's blur region, possibly breaking rounded corners, in which case you need to manually specify the corner radius in the *Rounded corners* section. | +| **Treat windows as transparent** | Whether force-blurred windows should be considered as transparent in order to fix transparency for applications that don't mark their windows as transparent. See #38 for an example. | -### Paint windows as non-opaque -Fixes transparency for some applications by marking their windows as transparent. This will only be done for force-blurred windows. +## Rounded corners +| Option | Description | +|------------------------------|----------------------------------------------------------------------------------------| +| **Window top corner radius** | Requires *Blur window decorations as well* to be enabled for windows with decorations. | +| **Anti-aliasing** | Makes rounded corners appear smoother. | -# Fake blur -When enabled, the blur texture will be cached and reused. The blurred areas of the window will be marked as opaque, resulting in KWin not painting anything behind them. -Only one image per screen is cached at a time. +## Static blur +When enabled, a cached texture will be painted behind the window instead of actually blurring the background (dynamic blur). +The blurred areas of the window will also be marked as opaque, causing KWin to not paint anything behind the window. +Both things result in lower resource usage. -Fake blur is mainly intended for laptop users who want longer battery life while still having blur everywhere. +Static blur is mainly intended for laptop users who want longer battery life while still having blur everywhere. -### Use real blur for windows that are in front of other windows -By default, when two windows overlap, you won't be able to see the window behind. -![image](https://github.com/taj-ny/kwin-effects-forceblur/assets/79316397/e581b5c1-7b2c-41c4-b180-4da5306747e1) +| Option | Description | +|-------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Use dynamic blur for windows with another window behind** | Whether to switch to dynamic blur when a window has another window behind it, allowing you to see the one behind.

Enabled:

Disabled:
| +| **Texture source** | The image to use for static blur.
- Desktop wallpaper - A screenshot of the desktop is taken for every screen. Icons and widgets will be included. The cached texture is invalidated when the entire desktop is repainted, which can happen when the wallpaper changes, icons are interacted with or when widgets update.
- Custom - The specified image is scaled for every screen without respecting the aspect ratio. Supported formats are JPEG and PNG. | +| **Blur texture** | Whether to blur the texture. This is only done once during texture creation. | -If this option is enabled, the effect will automatically switch to real blur when necessary. At very high blur strengths, there may be a slight difference in the texture. +## Window rules -https://github.com/taj-ny/kwin-effects-forceblur/assets/79316397/7bae6a16-6c78-4889-8df1-feb24005dabc +| Option | Description | +|-------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Convert simple config to window rules** | Whether to automatically convert simple configuration into window rules. Disabling this will cause most options in the simple configuration UI to not have an effect anymore. | -### Image source -The image to use for fake blur. +# Window rules +Configuration UI for window rules will be added in v2. The configuration file is located at ``~/.config/kwinbetterblurrc`` and isn't created automatically. -- Desktop wallpaper - A screenshot of the desktop is taken for every screen. Icons and widgets will be included. The cached texture is invalidated when the entire desktop is repainted, -which can happen when the wallpaper changes, icons are interacted with or when widgets update. -- Custom - The specified image is scaled for every screen without respecting the aspect ratio. Supported formats are JPEG and PNG. +Simple configuration is converted into multiple rules with priorities lower than 0. They are not added into the configuration file. This behavior can be disabled in the configuration UI in the *Window Rules* tab. -### Blur image -Whether to blur the image used for fake blur. This is only done once. +An example is provided at the end. + +## File structure +### Window rule +**Group**: [WindowRules][\$RuleName], where **$RuleName (string)** is the unique name of this rule. + +Rules are not evaluated in order as they appear in the file. + +| Property | Type | Description | +|--------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------| +| **Priority** | ``int (>=0)`` | Properties of rules with higher priority override properties of rules with lower priority.

Priorities lower than 0 are reserved. | + +### Conditions +**Group**: [WindowRules][\$RuleName][Conditions][\$ConditionName], where **$ConditionName (string)** is the unique name of this condition in the rule. + +At least 1 (or 0 if none specified) condition must be satisfied in order for this rule to be applied. All specified subconditions in a condition must be satisfied. + +| Property | Type | Description | +|-----------------------------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Negate** | ``enum (WindowClass, WindowState, WindowType)``
Multiple values are allowed. | Which subconditions should be negated.

Separated by space. | +| **HasWindowBehind** | ``bool`` | Whether only windows that (don't) have another window (excluding desktops) behind them should satisfy this condition. | +| **WindowClass** | ``string`` or ``regex`` | A regular expression executed on the window class. Only a partial match is required.

For an exact match, use ``^class$``.
To specify multiple classes, separate them by ``\|``: ``class1\|class2``.
A dot matches any character, which can be prevented by escaping it: ``\.``. | - | +| **WindowState** | ``enum (Fullscreen, Maximized)``
Multiple values are allowed. | Separated by space. | +| **WindowType** | ``enum (Dialog, Dock, Normal, Menu, Toolbar, Tooltip, Utility)``
Multiple values are allowed. | Separated by space. + +### Properties +**Group**: [WindowRules][\$RuleName][Properties] + +Properties don't have default values. Unset properties don't override properties of other rules. + +| Property | Type | Description | +|:-----------------------------|------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **BlurContent** | ``bool`` | Whether to force blur the window's content, excluding decorations. | +| **BlurDecorations** | ``bool`` | See *Blur window decorations as well* in the *Simple* section. The difference is that in window rules it's possible to blur only the decorations and the corner radius is specified in the *CornerRadius* property. | +| **CornerRadius** | ``float (>= 0)`` | Corner radius for the blur region.

A single number specifies the same radius for both top and bottom corners. Example: ``12.5``
Two numbers separated by a space specify different radii for top and bottom corners respectively. Example: ``0 12.5``

Top corner radius will only be applied to windows with force-blurred decorations or no decorations. | +| **CornerAntialiasing** | ``float (>= 0)`` | Makes rounded corners appear smoother. Recommended value is 1. | +| **ForceTransparency** | ``bool`` | See *Treat windows as transparent* in the *Simple* section. | +| **StaticBlur** | ``bool`` | Whether to use static blur instead of dynamic blur for the window. +| **WindowOpacityAffectsBlur** | ``bool`` | See *Window opacity affects blur* in the *Simple* section. | + +## Example +``` +[WindowRules][AllWindows][Properties] +CornerAntialiasing = true +WindowOpacityAffectsBlur = false + + +[WindowRules][ForceBlur][Conditions][0] +WindowClass = clementine|kate|kwrite|org\.freedesktop\.impl\.portal\.desktop\.kde|plasmashell +WindowType = Dialog Normal Menu Toolbar Tooltip Utility + +[WindowRules][ForceBlur][Conditions][1] +WindowClass = firefox +WindowType = Normal + +[WindowRules][ForceBlur][Properties] +BlurContent = true +BlurDecorations = true + + +[WindowRules][StaticBlur][Conditions][0] +HasWindowBehind = false + +[WindowRules][StaticBlur][Properties] +StaticBlur = true + + +[WindowRules][WindowCorners][Conditions][0] +Negate = WindowState +WindowState = Fullscreen Maximized +WindowType = Normal + +[WindowRules][WindowCorners][Properties] +CornerRadius = 15 + + +[WindowRules][MenuCorners][Conditions][0] +WindowType = Menu + +[WindowRules][MenuCorners][Properties] +CornerRadius = 10 +``` \ No newline at end of file diff --git a/flake.lock b/flake.lock index 71f15f8dba..5a7324ca64 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1730200266, - "narHash": "sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU=", + "lastModified": 1731139594, + "narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "807e9154dcb16384b1b765ebe9cd2bba2ac287fd", + "rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2", "type": "github" }, "original": { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2ce5f865c7..641f3eb292 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,11 +16,15 @@ set(forceblur_SOURCES blur.cpp blur.qrc main.cpp - settings.cpp + windowrules/windowrulecondition.cpp + windowrules/windowrule.cpp + windowrules/windowrulelist.cpp + windowrules/windowproperties.cpp + blurwindow.cpp ) kconfig_add_kcfg_files(forceblur_SOURCES - blurconfig.kcfgc + config/config.kcfgc ) add_library(forceblur MODULE ${forceblur_SOURCES}) @@ -31,5 +35,6 @@ target_link_libraries(forceblur PRIVATE KDecoration2::KDecoration ) +target_compile_definitions(forceblur PRIVATE CONFIG_KWIN) install(TARGETS forceblur DESTINATION ${KDE_INSTALL_PLUGINDIR}/kwin/effects/plugins) diff --git a/src/blur.cpp b/src/blur.cpp index d156dc4e0f..d86c93e47e 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -8,7 +8,7 @@ #include "blur.h" // KConfigSkeleton -#include "blurconfig.h" +#include "config.h" #include "core/pixelgrid.h" #include "core/rendertarget.h" @@ -16,11 +16,13 @@ #include "effect/effecthandler.h" #include "opengl/glutils.h" #include "opengl/glplatform.h" +#include "scene/windowitem.h" #include "utils.h" #include "utils/xcbutils.h" #include "wayland/blur.h" #include "wayland/display.h" #include "wayland/surface.h" +#include "kwin/window.h" #include #include @@ -53,10 +55,12 @@ static const QByteArray s_blurAtomName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEH BlurManagerInterface *BlurEffect::s_blurManager = nullptr; QTimer *BlurEffect::s_blurManagerRemoveTimer = nullptr; +std::vector BlurEffect::s_allWindows; BlurEffect::BlurEffect() { - BlurConfig::instance(effects->config()); + m_config = std::make_unique(); + BetterBlur::Config::instance(effects->config()); ensureResources(); m_downsamplePass.shader = ShaderManager::instance()->generateShaderFromFile(ShaderTrait::MapTexture, @@ -221,131 +225,42 @@ void BlurEffect::initBlurStrengthValues() void BlurEffect::reconfigure(ReconfigureFlags flags) { - m_settings.read(); + Q_UNUSED(flags) - m_iterationCount = blurStrengthValues[m_settings.general.blurStrength].iteration; - m_offset = blurStrengthValues[m_settings.general.blurStrength].offset; + BetterBlur::Config::self()->read(); + m_config->load(); + + int blurStrength = BetterBlur::Config::blurStrength() - 1; + m_iterationCount = blurStrengthValues[blurStrength].iteration; + m_offset = blurStrengthValues[blurStrength].offset; m_expandSize = blurOffsets[m_iterationCount - 1].expandSize; - m_fakeBlurTextures.clear(); + m_noiseStrength = BetterBlur::Config::noiseStrength(); + m_staticBlurImage = QImage(BetterBlur::Config::fakeBlurImage()); + m_staticBlurTextures.clear(); - for (EffectWindow *w : effects->stackingOrder()) { - updateBlurRegion(w); + for (const auto &[_, w] : m_windows) { + w->updateProperties(); + w->updateBlurRegion(); } // Update all windows for the blur to take effect effects->addRepaintFull(); } -void BlurEffect::updateBlurRegion(EffectWindow *w, bool geometryChanged) -{ - std::optional content; - std::optional frame; - - if (net_wm_blur_region != XCB_ATOM_NONE) { - const QByteArray value = w->readProperty(net_wm_blur_region, XCB_ATOM_CARDINAL, 32); - QRegion region; - if (value.size() > 0 && !(value.size() % (4 * sizeof(uint32_t)))) { - const uint32_t *cardinals = reinterpret_cast(value.constData()); - for (unsigned int i = 0; i < value.size() / sizeof(uint32_t);) { - int x = cardinals[i++]; - int y = cardinals[i++]; - int w = cardinals[i++]; - int h = cardinals[i++]; - region += Xcb::fromXNative(QRect(x, y, w, h)).toRect(); - } - } - if (!value.isNull()) { - content = region; - } - } - - SurfaceInterface *surf = w->surface(); - - if (surf && surf->blur()) { - content = surf->blur()->region(); - } - - if (auto internal = w->internalWindow()) { - const auto property = internal->property("kwin_blur"); - if (property.isValid()) { - content = property.value(); - } - } - - if (w->decorationHasAlpha() && decorationSupportsBlurBehind(w)) { - frame = decorationBlurRegion(w); - } - - // Don't override blur region for menus that already have one. The window geometry could include shadows. - if (shouldForceBlur(w) && !((isMenu(w) || w->isTooltip()) && (content.has_value() || geometryChanged))) { - content = w->expandedGeometry().translated(-w->x(), -w->y()).toRect(); - if (m_settings.forceBlur.blurDecorations && w->decoration()) { - frame = w->frameGeometry().translated(-w->x(), -w->y()).toRect(); - } - } - - if (content.has_value() || frame.has_value()) { - BlurEffectData &data = m_windows[w]; - data.content = content; - data.frame = frame; - } else if (!geometryChanged) { // Blur may disappear if this method is called when window geometry changes - if (auto it = m_windows.find(w); it != m_windows.end()) { - effects->makeOpenGLContextCurrent(); - m_windows.erase(it); - } - } -} - -bool BlurEffect::hasFakeBlur(EffectWindow *w) -{ - if (!m_settings.fakeBlur.enable) { - return false; - } - - if (m_settings.fakeBlur.disableWhenWindowBehind) { - if (auto it = m_windows.find(w); it != m_windows.end()) { - return !it->second.hasWindowBehind; - } - } - - return true; -} - void BlurEffect::slotWindowAdded(EffectWindow *w) { - SurfaceInterface *surf = w->surface(); - - if (surf) { - windowBlurChangedConnections[w] = connect(surf, &SurfaceInterface::blurChanged, this, [this, w]() { - if (w) { - updateBlurRegion(w); - } - }); - } + s_allWindows.push_back(w); + m_windows[w] = std::make_unique(m_config.get(), w, net_wm_blur_region); windowExpandedGeometryChangedConnections[w] = connect(w, &EffectWindow::windowExpandedGeometryChanged, this, [this,w]() { - if (!w) { - return; - } - - if (w->isDesktop() && !effects->waylandDisplay()) { - m_fakeBlurTextures.erase(nullptr); - return; + if (w && w->isDesktop() && !effects->waylandDisplay()) { + m_staticBlurTextures.erase(nullptr); } - - updateBlurRegion(w, true); }); if (auto internal = w->internalWindow()) { internal->installEventFilter(this); } - - connect(w, &EffectWindow::windowDecorationChanged, this, &BlurEffect::setupDecorationConnections); - setupDecorationConnections(w); - - updateBlurRegion(w); - - m_allWindows.push_back(w); } void BlurEffect::slotWindowDeleted(EffectWindow *w) @@ -354,41 +269,35 @@ void BlurEffect::slotWindowDeleted(EffectWindow *w) effects->makeOpenGLContextCurrent(); m_windows.erase(it); } - if (auto it = windowBlurChangedConnections.find(w); it != windowBlurChangedConnections.end()) { - disconnect(*it); - windowBlurChangedConnections.erase(it); - } if (auto it = windowExpandedGeometryChangedConnections.find(w); it != windowExpandedGeometryChangedConnections.end()) { disconnect(*it); windowExpandedGeometryChangedConnections.erase(it); } - if (auto it = std::find(m_allWindows.begin(), m_allWindows.end(), w); it != m_allWindows.end()) { - m_allWindows.erase(it); + if (auto it = std::find(s_allWindows.begin(), s_allWindows.end(), w); it != s_allWindows.end()) { + s_allWindows.erase(it); } - if (m_blurWhenTransformed.contains(w)) { - m_blurWhenTransformed.removeOne(w); - } + m_windows.erase(w); } void BlurEffect::slotScreenAdded(KWin::Output *screen) { screenChangedConnections[screen] = connect(screen, &Output::changed, this, [this, screen]() { - if (!m_settings.fakeBlur.enable) { + if (m_staticBlurTextures.empty()) { return; } - m_fakeBlurTextures.erase(screen); + m_staticBlurTextures.erase(screen); effects->addRepaintFull(); }); } void BlurEffect::slotScreenRemoved(KWin::Output *screen) { - for (auto &[window, data] : m_windows) { - if (auto it = data.render.find(screen); it != data.render.end()) { + for (auto &[_, w] : m_windows) { + if (auto it = w->render.find(screen); it != w->render.end()) { effects->makeOpenGLContextCurrent(); - data.render.erase(it); + w->render.erase(it); } } @@ -401,21 +310,10 @@ void BlurEffect::slotScreenRemoved(KWin::Output *screen) void BlurEffect::slotPropertyNotify(EffectWindow *w, long atom) { if (w && atom == net_wm_blur_region && net_wm_blur_region != XCB_ATOM_NONE) { - updateBlurRegion(w); + m_windows[w]->updateBlurRegion(); } } -void BlurEffect::setupDecorationConnections(EffectWindow *w) -{ - if (!w->decoration()) { - return; - } - - connect(w->decoration(), &KDecoration2::Decoration::blurRegionChanged, this, [this, w]() { - updateBlurRegion(w); - }); -} - bool BlurEffect::eventFilter(QObject *watched, QEvent *event) { auto internal = qobject_cast(watched); @@ -423,7 +321,7 @@ bool BlurEffect::eventFilter(QObject *watched, QEvent *event) QDynamicPropertyChangeEvent *pe = static_cast(event); if (pe->propertyName() == "kwin_blur") { if (auto w = effects->findWindow(internal)) { - updateBlurRegion(w); + m_windows[w]->updateBlurRegion(); } } } @@ -444,52 +342,11 @@ bool BlurEffect::supported() #endif } -bool BlurEffect::decorationSupportsBlurBehind(const EffectWindow *w) const -{ - return w->decoration() && !w->decoration()->blurRegion().isNull(); -} - -QRegion BlurEffect::decorationBlurRegion(const EffectWindow *w) const -{ - if (!decorationSupportsBlurBehind(w)) { - return QRegion(); - } - - QRegion decorationRegion = QRegion(w->decoration()->rect()) - w->contentsRect().toRect(); - //! we return only blurred regions that belong to decoration region - return decorationRegion.intersected(w->decoration()->blurRegion()); -} - -QRegion BlurEffect::blurRegion(EffectWindow *w) const -{ - QRegion region; - - if (auto it = m_windows.find(w); it != m_windows.end()) { - const std::optional &content = it->second.content; - const std::optional &frame = it->second.frame; - if (content.has_value()) { - if (content->isEmpty()) { - // An empty region means that the blur effect should be enabled - // for the whole window. - region = w->rect().toRect(); - } else { - if (frame.has_value()) { - region = frame.value(); - } - region += content->translated(w->contentsRect().topLeft().toPoint()) & w->contentsRect().toRect(); - } - } else if (frame.has_value()) { - region = frame.value(); - } - } - - return region; -} - void BlurEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) { - m_paintedArea = QRegion(); - m_currentBlur = QRegion(); + m_paintedArea = {}; + m_currentBlur = {}; + m_windowGeometriesSum = {}; m_currentScreen = effects->waylandDisplay() ? data.screen : nullptr; effects->prePaintScreen(data, presentTime); @@ -499,24 +356,26 @@ void BlurEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std:: { // this effect relies on prePaintWindow being called in the bottom to top order - // in case this window has regions to be blurred - const QRegion blurArea = blurRegion(w).translated(w->pos().toPoint()); - - bool fakeBlur = hasFakeBlur(w) && m_fakeBlurTextures.contains(m_currentScreen) && !blurArea.isEmpty(); - if (fakeBlur) { - data.opaque += blurArea; - - int topCornerRadius; - int bottomCornerRadius; - if (isMenu(w)) { - topCornerRadius = bottomCornerRadius = std::ceil(m_settings.roundedCorners.menuRadius); - } else if (w->isDock()) { - topCornerRadius = bottomCornerRadius = std::ceil(m_settings.roundedCorners.dockRadius); - } else { - topCornerRadius = std::ceil(m_settings.roundedCorners.windowTopRadius); - bottomCornerRadius = std::ceil(m_settings.roundedCorners.windowBottomRadius); + const auto &blurWindow = m_windows[w]; + + if (!w->isDesktop()) { + const auto hadWindowBehind = blurWindow->hasWindowBehind(); + blurWindow->setHasWindowBehind(m_windowGeometriesSum.intersects(w->frameGeometry().toRect())); + if (hadWindowBehind != blurWindow->hasWindowBehind()) { + data.paint += w->windowItem()->boundingRect().toRect(); + } + } + + const QRegion blurArea = blurWindow->blurRegion().translated(w->pos().toPoint()); + + bool staticBlur = blurWindow->properties()->staticBlur() && m_staticBlurTextures.contains(m_currentScreen) && !blurArea.isEmpty(); + if (staticBlur) { + if (!blurWindow->properties()->windowOpacityAffectsBlur()) { + data.opaque += blurArea; } + const int topCornerRadius = std::ceil(blurWindow->properties()->topCornerRadius()); + const int bottomCornerRadius = std::ceil(blurWindow->properties()->bottomCornerRadius()); if (!w->isDock() || (w->isDock() && isDockFloating(w, blurArea))) { const QRect blurRect = blurArea.boundingRect(); data.opaque -= QRect(blurRect.x(), blurRect.y(), topCornerRadius, topCornerRadius); @@ -526,46 +385,17 @@ void BlurEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std:: } } - if (m_settings.fakeBlur.enable) { - if (m_settings.fakeBlur.disableWhenWindowBehind) { - if (auto it = m_windows.find(w); it != m_windows.end()) { - const bool hadWindowBehind = it->second.hasWindowBehind; - it->second.hasWindowBehind = false; - for (EffectWindow *other : m_allWindows) { - if (w->window()->stackingOrder() <= other->window()->stackingOrder() - || other->isDesktop() - || !other->isOnCurrentDesktop() - || !other->isOnCurrentActivity() - || other->window()->resourceClass() == "xwaylandvideobridge" - || other->isMinimized()) { - continue; - } - - if (w->frameGeometry().intersects(other->frameGeometry())) { - it->second.hasWindowBehind = true; - break; - } - } - - if (hadWindowBehind != it->second.hasWindowBehind) { - data.paint += blurArea; - data.opaque -= blurArea; - } - } - } - - if (m_settings.fakeBlur.imageSource == FakeBlurImageSource::DesktopWallpaper && w->isDesktop() && w->frameGeometry() == data.paint.boundingRect()) { - m_fakeBlurTextures.erase(m_currentScreen); - } + if (!m_staticBlurTextures.empty() && w->isDesktop() && w->frameGeometry() == data.paint.boundingRect()) { + m_staticBlurTextures.erase(m_currentScreen); } - if (m_settings.forceBlur.markWindowAsTranslucent && !fakeBlur && shouldForceBlur(w)) { + if (!staticBlur && blurWindow->properties()->forceTransparency()) { data.setTranslucent(); } effects->prePaintWindow(w, data, presentTime); - if (!fakeBlur) { + if (!staticBlur) { const QRegion oldOpaque = data.opaque; if (data.opaque.intersects(m_currentBlur)) { // to blur an area partially we have to shrink the opaque area of a window @@ -601,67 +431,26 @@ void BlurEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std:: m_paintedArea -= data.opaque; m_paintedArea += data.paint; -} - -bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) -{ - const bool hasForceBlurRole = w->data(WindowForceBlurRole).toBool(); - if ((effects->activeFullScreenEffect() && !hasForceBlurRole) || w->isDesktop()) { - return false; - } - - bool scaled = !qFuzzyCompare(data.xScale(), 1.0) && !qFuzzyCompare(data.yScale(), 1.0); - bool translated = data.xTranslation() || data.yTranslation(); - if (!(scaled || (translated || (mask & PAINT_WINDOW_TRANSFORMED)))) { - if (m_blurWhenTransformed.contains(w)) { - m_blurWhenTransformed.removeOne(w); - } - - return true; - } - - // The force blur role may be removed while the window is still transformed, causing the blur to disappear for - // a short time. To avoid that, we allow the window to be blurred until it's not transformed anymore. - if (m_blurWhenTransformed.contains(w)) { - return true; - } else if (hasForceBlurRole) { - m_blurWhenTransformed.append(w); + if (!w->isDesktop() && w->window()->resourceClass() != "xwaylandvideobridge") { + m_windowGeometriesSum += w->frameGeometry().toRect(); } - - return hasForceBlurRole; -} - -bool BlurEffect::shouldForceBlur(const EffectWindow *w) const -{ - if (w->isDesktop() || (!m_settings.forceBlur.blurDocks && w->isDock()) || (!m_settings.forceBlur.blurMenus && isMenu(w))) { - return false; - } - - bool matches = m_settings.forceBlur.windowClasses.contains(w->window()->resourceName()) - || m_settings.forceBlur.windowClasses.contains(w->window()->resourceClass()); - return (matches && m_settings.forceBlur.windowClassMatchingMode == WindowClassMatchingMode::Whitelist) - || (!matches && m_settings.forceBlur.windowClassMatchingMode == WindowClassMatchingMode::Blacklist); } void BlurEffect::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data) { - auto it = m_windows.find(w); - if (it != m_windows.end()) { - BlurEffectData &blurInfo = it->second; - BlurRenderData &renderInfo = blurInfo.render[m_currentScreen]; - if (shouldBlur(w, mask, data)) { - blur(renderInfo, renderTarget, viewport, w, mask, region, data); - } + auto blurWindow = m_windows[w].get(); + if (blurWindow->shouldBlur(mask, data)) { + blur(blurWindow, blurWindow->render[m_currentScreen], renderTarget, viewport, mask, region, data); } // Draw the window over the blurred area effects->drawWindow(renderTarget, viewport, w, mask, region, data); } -GLTexture *BlurEffect::ensureFakeBlurTexture(const Output *output, const RenderTarget &renderTarget) +GLTexture *BlurEffect::ensureStaticBlurTexture(const Output *output, const RenderTarget &renderTarget) { - if (m_fakeBlurTextures.contains(output)) { - return m_fakeBlurTextures[output].get(); + if (m_staticBlurTextures.contains(output)) { + return m_staticBlurTextures[output].get(); } if (effects->waylandDisplay() && !output) { @@ -673,23 +462,23 @@ GLTexture *BlurEffect::ensureFakeBlurTexture(const Output *output, const RenderT textureFormat = renderTarget.texture()->internalFormat(); } GLTexture *texture = effects->waylandDisplay() - ? createFakeBlurTextureWayland(output, renderTarget, textureFormat) - : createFakeBlurTextureX11(textureFormat); + ? createStaticBlurTextureWayland(output, renderTarget, textureFormat) + : createStaticBlurTextureX11(textureFormat); if (!texture) { return nullptr; } - return (m_fakeBlurTextures[output] = std::unique_ptr(texture)).get(); + return (m_staticBlurTextures[output] = std::unique_ptr(texture)).get(); } GLTexture *BlurEffect::ensureNoiseTexture() { - if (m_settings.general.noiseStrength == 0) { + if (m_noiseStrength == 0) { return nullptr; } const qreal scale = std::max(1.0, QGuiApplication::primaryScreen()->logicalDotsPerInch() / 96.0); - if (!noiseTexture || noiseTextureScale != scale || noiseTextureStength != m_settings.general.noiseStrength) { + if (!noiseTexture || noiseTextureScale != scale || noiseTextureStength != m_noiseStrength) { // Init randomness based on time std::srand((uint)QTime::currentTime().msec()); @@ -699,7 +488,7 @@ GLTexture *BlurEffect::ensureNoiseTexture() uint8_t *noiseImageLine = (uint8_t *)noiseImage.scanLine(y); for (int x = 0; x < noiseImage.width(); x++) { - noiseImageLine[x] = std::rand() % m_settings.general.noiseStrength; + noiseImageLine[x] = std::rand() % m_noiseStrength; } } @@ -712,16 +501,18 @@ GLTexture *BlurEffect::ensureNoiseTexture() noiseTexture->setFilter(GL_NEAREST); noiseTexture->setWrapMode(GL_REPEAT); noiseTextureScale = scale; - noiseTextureStength = m_settings.general.noiseStrength; + noiseTextureStength = m_noiseStrength; } return noiseTexture.get(); } -void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data) +void BlurEffect::blur(BetterBlur::Window *w, BetterBlur::BlurRenderData &renderInfo, const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, WindowPaintData &data) { + Q_UNUSED(mask) + // Compute the effective blur shape. Note that if the window is transformed, so will be the blur shape. - QRegion blurShape = w ? blurRegion(w).translated(w->pos().toPoint()) : region; + QRegion blurShape = w ? w->blurRegion().translated(w->window()->pos().toPoint()) : region; if (data.xScale() != 1 || data.yScale() != 1) { QPoint pt = blurShape.boundingRect().topLeft(); QRegion scaledShape; @@ -739,8 +530,8 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg const QRect backgroundRect = blurShape.boundingRect(); const QRect deviceBackgroundRect = snapToPixelGrid(scaledRect(backgroundRect, viewport.scale())); - const auto opacity = w && m_settings.general.windowOpacityAffectsBlur - ? w->opacity() * data.opacity() + const auto opacity = w && w->properties()->windowOpacityAffectsBlur() + ? w->window()->opacity() * data.opacity() : data.opacity(); QList effectiveShape; @@ -767,20 +558,11 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg float topCornerRadius = 0; float bottomCornerRadius = 0; - if (w && !(w->isDock() && !isDockFloating(w, blurShape))) { - const bool isMaximized = effects->clientArea(MaximizeArea, effects->activeScreen(), effects->currentDesktop()) == w->frameGeometry(); - if (isMenu(w)) { - topCornerRadius = bottomCornerRadius = m_settings.roundedCorners.menuRadius; - } else if (w->isDock()) { - topCornerRadius = bottomCornerRadius = m_settings.roundedCorners.dockRadius; - } else if ((!w->isFullScreen() && !isMaximized) || m_settings.roundedCorners.roundMaximized) { - if (!w->decoration() || (w->decoration() && m_settings.forceBlur.blurDecorations)) { - topCornerRadius = m_settings.roundedCorners.windowTopRadius; - } - bottomCornerRadius = m_settings.roundedCorners.windowBottomRadius; + if (w && !(w->window()->isDock() && !isDockFloating(w->window(), blurShape))) { + if (!w->window()->decoration() || (w->window()->decoration() && w->properties()->blurDecorations())) { + topCornerRadius = w->properties()->topCornerRadius() * viewport.scale(); } - topCornerRadius = topCornerRadius * viewport.scale(); - bottomCornerRadius = bottomCornerRadius * viewport.scale(); + bottomCornerRadius = w->properties()->bottomCornerRadius() * viewport.scale(); } // Maybe reallocate offscreen render targets. Keep in mind that the first one contains @@ -792,12 +574,12 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg // Since the VBO is shared, the texture needs to be blurred before the geometry is uploaded, otherwise it will be // reset. - GLTexture *fakeBlurTexture = nullptr; - if (w && hasFakeBlur(w)) { - fakeBlurTexture = ensureFakeBlurTexture(m_currentScreen, renderTarget); + GLTexture *staticBlurTexture = nullptr; + if (w && w->properties()->staticBlur()) { + staticBlurTexture = ensureStaticBlurTexture(m_currentScreen, renderTarget); } - if (!fakeBlurTexture + if (!staticBlurTexture && (renderInfo.framebuffers.size() != (m_iterationCount + 1) || renderInfo.textures[0]->size() != backgroundRect.size() || renderInfo.textures[0]->internalFormat() != textureFormat)) { @@ -824,7 +606,7 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg } // Fetch the pixels behind the shape that is going to be blurred. - if (!fakeBlurTexture) { + if (!staticBlurTexture) { const QRegion dirtyRegion = region & backgroundRect; for (const QRect &dirtyRect: dirtyRegion) { renderInfo.framebuffers[0]->blitFromRenderTarget(renderTarget, viewport, dirtyRect, dirtyRect.translated(-backgroundRect.topLeft())); @@ -935,7 +717,7 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg vbo->bindArrays(); - if (fakeBlurTexture) { + if (staticBlurTexture) { ShaderManager::instance()->pushShader(m_texture.shader.get()); QMatrix4x4 projectionMatrix; @@ -948,16 +730,16 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg } m_texture.shader->setUniform(m_texture.mvpMatrixLocation, projectionMatrix); - m_texture.shader->setUniform(m_texture.textureSizeLocation, QVector2D(fakeBlurTexture->size().width(), fakeBlurTexture->size().height())); + m_texture.shader->setUniform(m_texture.textureSizeLocation, QVector2D(staticBlurTexture->size().width(), staticBlurTexture->size().height())); m_texture.shader->setUniform(m_texture.texStartPosLocation, QVector2D(backgroundRect.x() - screenGeometry.x(), backgroundRect.y() - screenGeometry.y())); m_texture.shader->setUniform(m_texture.blurSizeLocation, QVector2D(backgroundRect.width(), backgroundRect.height())); m_texture.shader->setUniform(m_texture.scaleLocation, (float)viewport.scale()); m_texture.shader->setUniform(m_texture.topCornerRadiusLocation, topCornerRadius); m_texture.shader->setUniform(m_texture.bottomCornerRadiusLocation, bottomCornerRadius); - m_texture.shader->setUniform(m_texture.antialiasingLocation, m_settings.roundedCorners.antialiasing); + m_texture.shader->setUniform(m_texture.antialiasingLocation, w->properties()->cornerAntialiasing()); m_texture.shader->setUniform(m_texture.opacityLocation, static_cast(opacity)); - fakeBlurTexture->bind(); + staticBlurTexture->bind(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -1023,7 +805,7 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg GLFramebuffer::popFramebuffer(); const auto &read = renderInfo.framebuffers[1]; - if (m_settings.general.noiseStrength > 0) { + if (m_noiseStrength > 0) { if (const auto *noiseTexture = ensureNoiseTexture()) { m_upsamplePass.shader->setUniform(m_upsamplePass.noiseLocation, true); m_upsamplePass.shader->setUniform(m_upsamplePass.noiseTextureSizeLocation, QVector2D(noiseTexture->width(), noiseTexture->height())); @@ -1039,7 +821,7 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg m_upsamplePass.shader->setUniform(m_upsamplePass.topCornerRadiusLocation, topCornerRadius); m_upsamplePass.shader->setUniform(m_upsamplePass.bottomCornerRadiusLocation, bottomCornerRadius); - m_upsamplePass.shader->setUniform(m_upsamplePass.antialiasingLocation, m_settings.roundedCorners.antialiasing); + m_upsamplePass.shader->setUniform(m_upsamplePass.antialiasingLocation, w ? w->properties()->cornerAntialiasing() : 0.0); m_upsamplePass.shader->setUniform(m_upsamplePass.blurSizeLocation, QVector2D(backgroundRect.width(), backgroundRect.height())); m_upsamplePass.shader->setUniform(m_upsamplePass.opacityLocation, static_cast(opacity)); @@ -1068,13 +850,13 @@ void BlurEffect::blur(GLTexture *texture) const QRect textureRect = QRect(0, 0, texture->width(), texture->height()); auto blurredFramebuffer = std::make_unique(texture); - BlurRenderData renderData; + BetterBlur::BlurRenderData renderInfo; const RenderTarget renderTarget(blurredFramebuffer.get()); const RenderViewport renderViewport(textureRect, 1.0, renderTarget); WindowPaintData data; GLFramebuffer::pushFramebuffer(blurredFramebuffer.get()); - blur(renderData, renderTarget, renderViewport, nullptr, 0, textureRect, data); + blur(nullptr, renderInfo, renderTarget, renderViewport, 0, textureRect, data); GLFramebuffer::popFramebuffer(); } @@ -1110,7 +892,7 @@ GLTexture *BlurEffect::wallpaper(EffectWindow *desktop, const qreal &scale, cons return texture.release(); } -GLTexture *BlurEffect::createFakeBlurTextureWayland(const Output *output, const RenderTarget &renderTarget, const GLenum &textureFormat) +GLTexture *BlurEffect::createStaticBlurTextureWayland(const Output *output, const RenderTarget &renderTarget, const GLenum &textureFormat) { EffectWindow *desktop = nullptr; for (EffectWindow *w : effects->stackingOrder()) { @@ -1124,10 +906,10 @@ GLTexture *BlurEffect::createFakeBlurTextureWayland(const Output *output, const } std::unique_ptr texture; - if (m_settings.fakeBlur.imageSource == FakeBlurImageSource::DesktopWallpaper) { + if (BetterBlur::Config::fakeBlurImageSourceDesktopWallpaper()) { texture.reset(wallpaper(desktop, output->scale(), textureFormat)); - } else if (m_settings.fakeBlur.imageSource == FakeBlurImageSource::Custom) { - texture = GLTexture::upload(m_settings.fakeBlur.customImage.scaled(output->pixelSize(), Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation)); + } else if (BetterBlur::Config::fakeBlurImageSourceCustom()) { + texture = GLTexture::upload(m_staticBlurImage.scaled(output->pixelSize(), Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation)); } if (!texture) { return nullptr; @@ -1162,14 +944,14 @@ GLTexture *BlurEffect::createFakeBlurTextureWayland(const Output *output, const GLFramebuffer::popFramebuffer(); ShaderManager::instance()->popShader(); - if (m_settings.fakeBlur.blurCustomImage) { + if (BetterBlur::Config::fakeBlurCustomImageBlur()) { blur(texture.get()); } return texture.release(); } -GLTexture *BlurEffect::createFakeBlurTextureX11(const GLenum &textureFormat) +GLTexture *BlurEffect::createStaticBlurTextureX11(const GLenum &textureFormat) { std::vector desktops; QRegion desktopGeometries; @@ -1196,16 +978,16 @@ GLTexture *BlurEffect::createFakeBlurTextureX11(const GLenum &textureFormat) const auto geometry = desktop->frameGeometry(); std::unique_ptr texture; - if (m_settings.fakeBlur.imageSource == FakeBlurImageSource::DesktopWallpaper) { + if (BetterBlur::Config::fakeBlurImageSourceDesktopWallpaper()) { texture.reset(wallpaper(desktop, 1, textureFormat)); - } else if (m_settings.fakeBlur.imageSource == FakeBlurImageSource::Custom) { - texture = GLTexture::upload(m_settings.fakeBlur.customImage.scaled(geometry.width(), geometry.height(), Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation)); + } else if (BetterBlur::Config::fakeBlurImageSourceCustom()) { + texture = GLTexture::upload(m_staticBlurImage.scaled(geometry.width(), geometry.height(), Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation)); } if (!texture) { return nullptr; } - if (m_settings.fakeBlur.blurCustomImage) { + if (BetterBlur::Config::fakeBlurCustomImageBlur()) { blur(texture.get()); } @@ -1222,6 +1004,11 @@ GLTexture *BlurEffect::createFakeBlurTextureX11(const GLenum &textureFormat) return compositeTexture.release(); } +const std::vector BlurEffect::allWindows() +{ + return BlurEffect::s_allWindows; +} + bool BlurEffect::isActive() const { return m_valid && !effects->isScreenLocked(); diff --git a/src/blur.h b/src/blur.h index bb06547f25..961d5b367a 100644 --- a/src/blur.h +++ b/src/blur.h @@ -7,10 +7,11 @@ #pragma once +#include "windowrules/windowrulelist.h" + #include "effect/effect.h" #include "opengl/glutils.h" -#include "settings.h" -#include "window.h" +#include "blurwindow.h" #include @@ -21,28 +22,6 @@ namespace KWin class BlurManagerInterface; -struct BlurRenderData -{ - /// Temporary render targets needed for the Dual Kawase algorithm, the first texture - /// contains not blurred background behind the window, it's cached. - std::vector> textures; - std::vector> framebuffers; -}; - -struct BlurEffectData -{ - /// The region that should be blurred behind the window - std::optional content; - - /// The region that should be blurred behind the frame - std::optional frame; - - /// The render data per screen. Screens can have different color spaces. - std::unordered_map render; - - bool hasWindowBehind; -}; - class BlurEffect : public KWin::Effect { Q_OBJECT @@ -54,6 +33,8 @@ class BlurEffect : public KWin::Effect static bool supported(); static bool enabledByDefault(); + static const std::vector allWindows(); + void reconfigure(ReconfigureFlags flags) override; void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override; void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) override; @@ -77,30 +58,22 @@ public Q_SLOTS: void slotScreenAdded(KWin::Output *screen); void slotScreenRemoved(KWin::Output *screen); void slotPropertyNotify(KWin::EffectWindow *w, long atom); - void setupDecorationConnections(EffectWindow *w); private: void initBlurStrengthValues(); - QRegion blurRegion(EffectWindow *w) const; - QRegion decorationBlurRegion(const EffectWindow *w) const; - bool decorationSupportsBlurBehind(const EffectWindow *w) const; - bool shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data); - bool shouldForceBlur(const EffectWindow *w) const; - void updateBlurRegion(EffectWindow *w, bool geometryChanged = false); - bool hasFakeBlur(EffectWindow *w); /* * @param w The pointer to the window being blurred, nullptr if an image is being blurred. */ - void blur(BlurRenderData &renderInfo, const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data); + void blur(BetterBlur::Window *w, BetterBlur::BlurRenderData &renderInfo, const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, WindowPaintData &data); void blur(GLTexture *texture); /** * @param output Can be nullptr. * @remark This method shall not be called outside of BlurEffect::blur. - * @return The cached fake blur texture. The texture will be created if it doesn't exist. + * @return The cached static blur texture. The texture will be created if it doesn't exist. */ - GLTexture *ensureFakeBlurTexture(const Output *output, const RenderTarget &renderTarget); + GLTexture *ensureStaticBlurTexture(const Output *output, const RenderTarget &renderTarget); GLTexture *ensureNoiseTexture(); /** @@ -111,17 +84,17 @@ public Q_SLOTS: GLTexture *wallpaper(EffectWindow *desktop, const qreal &scale, const GLenum &textureFormat); /** - * Creates a fake blur texture for the specified screen. + * Creates a static blur texture for the specified screen. * @remark This method shall not be called outside of BlurEffect::blur. * @return A pointer to the texture, or nullptr if an error occurred. */ - GLTexture *createFakeBlurTextureWayland(const Output *output, const RenderTarget &renderTarget, const GLenum &textureFormat); + GLTexture *createStaticBlurTextureWayland(const Output *output, const RenderTarget &renderTarget, const GLenum &textureFormat); /** - * Creates a composite fake blur texture containing images for all screens. + * Creates a composite static blur texture containing images for all screens. * @return A pointer to the texture, or nullptr if an error occurred. */ - GLTexture *createFakeBlurTextureX11(const GLenum &textureFormat); + GLTexture *createStaticBlurTextureX11(const GLenum &textureFormat); private: struct @@ -173,17 +146,17 @@ public Q_SLOTS: QRegion m_paintedArea; // keeps track of all painted areas (from bottom to top) QRegion m_currentBlur; // keeps track of the currently blured area of the windows(from bottom to top) Output *m_currentScreen = nullptr; + QRegion m_windowGeometriesSum; size_t m_iterationCount; // number of times the texture will be downsized to half size int m_offset; int m_expandSize; + int m_noiseStrength; std::unique_ptr noiseTexture; qreal noiseTextureScale = 1.0; int noiseTextureStength = 0; - BlurSettings m_settings; - struct OffsetStruct { float minOffset; @@ -201,15 +174,14 @@ public Q_SLOTS: QList blurStrengthValues; - std::unordered_map> m_fakeBlurTextures; - - // Windows to blur even when transformed. - QList m_blurWhenTransformed; + std::unordered_map> m_staticBlurTextures; + QImage m_staticBlurImage; - QMap windowBlurChangedConnections; QMap windowExpandedGeometryChangedConnections; QMap screenChangedConnections; - std::unordered_map m_windows; + std::unordered_map> m_windows; + + std::unique_ptr m_config; /** * Stores all currently open windows, even those that aren't blurred. Used for determining whether windows are @@ -218,7 +190,7 @@ public Q_SLOTS: * Objects retrieved from effects->stackingOrder() and workspace()->stackingOrder() appear to be deleted when * BlurEffect::prePaintWindow is running, so that can't be used. */ - std::vector m_allWindows; + static std::vector s_allWindows; static BlurManagerInterface *s_blurManager; static QTimer *s_blurManagerRemoveTimer; diff --git a/src/blurconfig.kcfgc b/src/blurconfig.kcfgc deleted file mode 100644 index 4fb3fe58ed..0000000000 --- a/src/blurconfig.kcfgc +++ /dev/null @@ -1,5 +0,0 @@ -File=blur.kcfg -ClassName=BlurConfig -NameSpace=KWin -Singleton=true -Mutators=true diff --git a/src/blurwindow.cpp b/src/blurwindow.cpp new file mode 100644 index 0000000000..fc2319b3ce --- /dev/null +++ b/src/blurwindow.cpp @@ -0,0 +1,256 @@ +#include "utils.h" +#include "blurwindow.h" + +#include "effect/effecthandler.h" +#include "effect/globals.h" +#include "effect/effectwindow.h" +#include "utils/xcbutils.h" +#include "wayland/blur.h" +#include "wayland/surface.h" +#include "window.h" + +#include + +namespace BetterBlur +{ + +Window::Window(const WindowRuleList *windowRules, KWin::EffectWindow *w, long net_wm_blur_region) + : m_windowRules(windowRules), + w(w), + net_wm_blur_region(net_wm_blur_region) +{ + connect(w, &KWin::EffectWindow::windowDecorationChanged, this, [this]() { + setupDecorationConnections(); + }); + windowExpandedGeometryChangedConnection = connect(w, &KWin::EffectWindow::windowExpandedGeometryChanged, this, &Window::slotWindowExpandedGeometryChanged); + setupDecorationConnections(); + + if (auto surf = w->surface()) { + windowBlurChangedConnection = connect(surf, &KWin::SurfaceInterface::blurChanged, [this]() { + updateBlurRegion(); + }); + } + + updateProperties(); + updateBlurRegion(); +} + +Window::~Window() +{ + disconnect(windowBlurChangedConnection); + disconnect(windowExpandedGeometryChangedConnection); +} + +bool Window::isMaximized() const +{ + return KWin::effects->clientArea(KWin::MaximizeArea, KWin::effects->activeScreen(), KWin::effects->currentDesktop()) == w->window()->frameGeometry(); +} + +bool Window::isMenu() const +{ + return w->isMenu() || w->isDropdownMenu() || w->isPopupMenu() || w->isPopupWindow(); +} + +void Window::updateBlurRegion(bool geometryChanged) +{ + std::optional content; + std::optional frame; + + if (net_wm_blur_region != XCB_ATOM_NONE) { + const QByteArray value = w->readProperty(net_wm_blur_region, XCB_ATOM_CARDINAL, 32); + QRegion region; + if (value.size() > 0 && !(value.size() % (4 * sizeof(uint32_t)))) { + const uint32_t *cardinals = reinterpret_cast(value.constData()); + for (unsigned int i = 0; i < value.size() / sizeof(uint32_t);) { + int x = cardinals[i++]; + int y = cardinals[i++]; + int w = cardinals[i++]; + int h = cardinals[i++]; + region += KWin::Xcb::fromXNative(QRect(x, y, w, h)).toRect(); + } + } + if (!value.isNull()) { + content = region; + } + } + + KWin::SurfaceInterface *surf = w->surface(); + + if (surf && surf->blur()) { + content = surf->blur()->region(); + } + + if (auto internal = w->internalWindow()) { + const auto property = internal->property("kwin_blur"); + if (property.isValid()) { + content = property.value(); + } + } + + if (w->decorationHasAlpha() && decorationSupportsBlurBehind()) { + frame = decorationBlurRegion(); + } + + // Don't override blur region for menus that already have one. The window geometry could include shadows. + if (!((isMenu() || w->isTooltip()) && (content.has_value() || geometryChanged))) { + if (m_properties->blurContent()) { + content = w->contentsRect().toRect(); + } + if (m_properties->blurDecorations() && w->decoration()) { + frame = QRegion(w->frameGeometry().translated(-w->x(), -w->y()).toRect()) - w->contentsRect().toRect(); + } + } + + if (content.has_value() || frame.has_value()) { + m_contentBlurRegion = content; + m_frameBlurRegion = frame; + } else if (!geometryChanged) { // Blur may disappear if this method is called when window geometry changes + m_contentBlurRegion = {}; + m_frameBlurRegion = {}; + render.clear(); + } +} + +QRegion Window::blurRegion() const +{ + QRegion region; + + if (m_contentBlurRegion.has_value()) { + if (m_contentBlurRegion->isEmpty()) { + // An empty region means that the blur effect should be enabled + // for the whole window. + region = w->rect().toRect(); + } else { + if (m_frameBlurRegion.has_value()) { + region = m_frameBlurRegion.value(); + } + region += m_contentBlurRegion->translated(w->contentsRect().topLeft().toPoint()) & w->contentsRect().toRect(); + } + } else if (m_frameBlurRegion.has_value()) { + region = m_frameBlurRegion.value(); + } + + return region; +} + +bool Window::shouldBlur(int mask, const KWin::WindowPaintData &data) +{ + const bool hasForceBlurRole = w->data(KWin::WindowForceBlurRole).toBool(); + if ((KWin::effects->activeFullScreenEffect() && !hasForceBlurRole) || w->isDesktop()) { + return false; + } + + bool scaled = !qFuzzyCompare(data.xScale(), 1.0) && !qFuzzyCompare(data.yScale(), 1.0); + bool translated = data.xTranslation() || data.yTranslation(); + if (!(scaled || (translated || (mask & KWin::Effect::PAINT_WINDOW_TRANSFORMED)))) { + m_blurWhenTransformed = false; + return true; + } + + // The force blur role may be removed while the window is still transformed, causing the blur to disappear for + // a short time. To avoid that, we allow the window to be blurred until it's not transformed anymore. + if (m_blurWhenTransformed) { + return true; + } else if (hasForceBlurRole) { + m_blurWhenTransformed = true; + } + + return hasForceBlurRole; +} + +QRegion Window::decorationBlurRegion() const +{ + if (!decorationSupportsBlurBehind()) { + return {}; + } + + QRegion decorationRegion = QRegion(w->decoration()->rect()) - w->contentsRect().toRect(); + //! we return only blurred regions that belong to decoration region + return decorationRegion.intersected(w->decoration()->blurRegion()); +} + +bool Window::decorationSupportsBlurBehind() const +{ + return w->decoration() && !w->decoration()->blurRegion().isNull(); +} + +void Window::slotWindowExpandedGeometryChanged(KWin::EffectWindow *w) +{ + if (w->size() != m_size) { + updateProperties(); + } + + updateBlurRegion(true); + m_size = w->size(); +} + +void Window::setupDecorationConnections() +{ + if (!w->decoration()) { + return; + } + + connect(w->decoration(), &KDecoration2::Decoration::blurRegionChanged, this, [this]() { + updateBlurRegion(); + }); +} + +void Window::updateProperties() +{ + // TODO Rule evaluation needs to be optimized. Sometimes it's not necessary to evaluate a rule. + + auto staticBlur = false; + if (m_properties) { + staticBlur = m_properties->staticBlur(); + } + + m_properties = std::make_unique(); + for (const auto &rule : m_windowRules->rules()) { + if (!rule->matches(this)) { + continue; + } + + auto ruleProperties = rule->properties(); + if (ruleProperties->m_blurContent) { + m_properties->setBlurContent(*ruleProperties->m_blurContent); + } + if (ruleProperties->m_blurDecorations) { + m_properties->setBlurDecorations(*ruleProperties->m_blurDecorations); + } + if (ruleProperties->m_forceTransparency) { + m_properties->setForceTransparency(*ruleProperties->m_forceTransparency); + } + if (ruleProperties->m_bottomCornerRadius) { + m_properties->setBottomCornerRadius(*ruleProperties->m_bottomCornerRadius); + } + if (ruleProperties->m_topCornerRadius) { + m_properties->setTopCornerRadius(*ruleProperties->m_topCornerRadius); + } + if (ruleProperties->m_cornerAntialiasing) { + m_properties->setCornerAntialiasing(*ruleProperties->m_cornerAntialiasing); + } + if (ruleProperties->m_staticBlur) { + m_properties->setStaticBlur(*ruleProperties->m_staticBlur); + } + if (ruleProperties->m_windowOpacityAffectsBlur) { + m_properties->setWindowOpacityAffectsBlur(*ruleProperties->m_windowOpacityAffectsBlur); + } + } + + if (staticBlur != m_properties->staticBlur()) { + w->addRepaintFull(); + } +} + +void Window::setHasWindowBehind(const bool &hasWindowBehind) +{ + const auto old = m_hasWindowBehind; + m_hasWindowBehind = hasWindowBehind; + if (old != m_hasWindowBehind) { + updateProperties(); + } +} + +} + +#include "moc_blurwindow.cpp" \ No newline at end of file diff --git a/src/blurwindow.h b/src/blurwindow.h new file mode 100644 index 0000000000..fd163bd770 --- /dev/null +++ b/src/blurwindow.h @@ -0,0 +1,83 @@ +#pragma once + +#include "windowrules/windowproperties.h" +#include "windowrules/windowrulelist.h" + +#include "effect/effect.h" +#include "effect/effectwindow.h" +#include "opengl/glutils.h" + +namespace BetterBlur +{ + +class WindowRuleList; + +struct BlurRenderData +{ + /// Temporary render targets needed for the Dual Kawase algorithm, the first texture + /// contains not blurred background behind the window, it's cached. + std::vector> textures; + std::vector> framebuffers; +}; + +class Window : public QObject +{ + Q_OBJECT +public: + Window(const WindowRuleList *windowRules, KWin::EffectWindow *w, long net_wm_blur_region); + ~Window() override; + + bool isMaximized() const; + bool isMenu() const; + + void updateBlurRegion(bool geometryChanged = false); + QRegion blurRegion() const; + bool shouldBlur(int mask, const KWin::WindowPaintData &data); + + const WindowProperties *properties() const + { + return m_properties.get(); + } + void updateProperties(); + + KWin::EffectWindow *window() const + { + return w; + } + + bool hasWindowBehind() const + { + return m_hasWindowBehind; + } + void setHasWindowBehind(const bool &hasWindowBehind); + + /// The render data per screen. Screens can have different color spaces. + std::unordered_map render; + +public Q_SLOTS: + void slotWindowExpandedGeometryChanged(KWin::EffectWindow *w); +// void slotDecorationBlurRegionChanged(); + +private: + void setupDecorationConnections(); + + QRegion decorationBlurRegion() const; + bool decorationSupportsBlurBehind() const; + + QMetaObject::Connection windowBlurChangedConnection; + QMetaObject::Connection windowExpandedGeometryChangedConnection; + + std::optional m_contentBlurRegion; + std::optional m_frameBlurRegion; + long net_wm_blur_region = 0; + bool m_blurWhenTransformed = false; + + QSizeF m_size; + bool m_hasWindowBehind = false; + std::unique_ptr m_properties; + KWin::EffectWindow *w; + + const WindowRuleList *m_windowRules; +}; + +} \ No newline at end of file diff --git a/src/blur.kcfg b/src/config/config.kcfg similarity index 95% rename from src/blur.kcfg rename to src/config/config.kcfg index 32512e8735..9fa9296b8b 100644 --- a/src/blur.kcfg +++ b/src/config/config.kcfg @@ -17,7 +17,7 @@ class1 class2 -class3 +$regex:class3 true @@ -73,5 +73,8 @@ class3 true + + true + diff --git a/src/config/config.kcfgc b/src/config/config.kcfgc new file mode 100644 index 0000000000..d56f762df0 --- /dev/null +++ b/src/config/config.kcfgc @@ -0,0 +1,5 @@ +File=config.kcfg +ClassName=Config +NameSpace=BetterBlur +Singleton=true +Mutators=true diff --git a/src/kcm/CMakeLists.txt b/src/kcm/CMakeLists.txt index 7072ba56b9..698a4a9cda 100644 --- a/src/kcm/CMakeLists.txt +++ b/src/kcm/CMakeLists.txt @@ -1,7 +1,7 @@ set(kwin_better_blur_config_SRCS blur_config.cpp blur_config.h) ki18n_wrap_ui(kwin_better_blur_config_SRCS blur_config.ui) -kconfig_add_kcfg_files(kwin_better_blur_config_SRCS ../blurconfig.kcfgc) +kconfig_add_kcfg_files(kwin_better_blur_config_SRCS ../config/config.kcfgc) qt_add_dbus_interface(kwin_better_blur_config_SRCS ${KWIN_EFFECTS_INTERFACE} kwineffects_interface) diff --git a/src/kcm/blur_config.cpp b/src/kcm/blur_config.cpp index 0f5fab180e..ee4ad6ee4c 100644 --- a/src/kcm/blur_config.cpp +++ b/src/kcm/blur_config.cpp @@ -8,7 +8,7 @@ //#include // KConfigSkeleton -#include "blurconfig.h" +#include "config.h" #include #include "kwineffects_interface.h" @@ -19,11 +19,11 @@ namespace KWin K_PLUGIN_CLASS(BlurEffectConfig) BlurEffectConfig::BlurEffectConfig(QObject *parent, const KPluginMetaData &data) - : KCModule(parent, data) + : KCModule(parent, data) { ui.setupUi(widget()); - BlurConfig::instance("kwinrc"); - addConfig(BlurConfig::self(), widget()); + BetterBlur::Config::instance("kwinrc"); + addConfig(BetterBlur::Config::self(), widget()); } BlurEffectConfig::~BlurEffectConfig() @@ -43,5 +43,4 @@ void BlurEffectConfig::save() } // namespace KWin #include "blur_config.moc" - #include "moc_blur_config.cpp" diff --git a/src/kcm/blur_config.ui b/src/kcm/blur_config.ui index cfd50e11ab..5787866491 100644 --- a/src/kcm/blur_config.ui +++ b/src/kcm/blur_config.ui @@ -199,7 +199,8 @@ - Classes of windows to force blur (one per line): + Classes of windows to force blur (one per line). +Use the "$regex:" prefix to specify regular expressions. @@ -391,14 +392,14 @@ - Fake Blur + Static Blur - When enabled, the blur texture will be cached and reused, resulting in much lower GPU usage. -Works best with tiling. + When enabled, a cached texture will be painted behind the window instead of +actually blurring the background, resulting in much lower resource usage. Works best with tiling. @@ -425,7 +426,7 @@ Works best with tiling. - Use real blur for windows that are in front of other windows + Use dynamic blur for windows with another window behind @@ -451,7 +452,7 @@ Works best with tiling. - Blur image + Blur texture (only done once) @@ -460,14 +461,14 @@ Works best with tiling. - Image source: + Texture source: - Desktop wallpaper + Desktop wallpaper (do not use with frequently-changing wallpapers) @@ -561,6 +562,45 @@ Works best with tiling. + + + Window Rules + + + + + + There is currently no configuration UI for window rules. +See the configuration documentation for more information. + + + + + + + + 0 + 0 + + + + Convert simple config to window rules (if disabled, most options won't have an effect +anymore and will need to be manually specified in window rules) + + + + + + + + 0 + 1 + + + + + + diff --git a/src/settings.cpp b/src/settings.cpp deleted file mode 100644 index 88eb0db6d5..0000000000 --- a/src/settings.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "settings.h" -#include "blurconfig.h" - -namespace KWin -{ - -void BlurSettings::read() -{ - BlurConfig::self()->read(); - - general.blurStrength = BlurConfig::blurStrength() - 1; - general.noiseStrength = BlurConfig::noiseStrength(); - general.windowOpacityAffectsBlur = BlurConfig::transparentBlur(); - - forceBlur.windowClasses = BlurConfig::windowClasses().split("\n"); - forceBlur.windowClassMatchingMode = BlurConfig::blurMatching() ? WindowClassMatchingMode::Whitelist : WindowClassMatchingMode::Blacklist; - forceBlur.blurDecorations = BlurConfig::blurDecorations(); - forceBlur.blurMenus = BlurConfig::blurMenus(); - forceBlur.blurDocks = BlurConfig::blurDocks(); - forceBlur.markWindowAsTranslucent = BlurConfig::paintAsTranslucent(); - - roundedCorners.windowTopRadius = BlurConfig::topCornerRadius(); - roundedCorners.windowBottomRadius = BlurConfig::bottomCornerRadius(); - roundedCorners.menuRadius = BlurConfig::menuCornerRadius(); - roundedCorners.dockRadius = BlurConfig::dockCornerRadius(); - roundedCorners.antialiasing = BlurConfig::roundedCornersAntialiasing(); - roundedCorners.roundMaximized = BlurConfig::roundCornersOfMaximizedWindows(); - - fakeBlur.enable = BlurConfig::fakeBlur(); - fakeBlur.disableWhenWindowBehind = BlurConfig::fakeBlurDisableWhenWindowBehind(); - fakeBlur.customImage = QImage(BlurConfig::fakeBlurImage()); - if (BlurConfig::fakeBlurImageSourceDesktopWallpaper()) { - fakeBlur.imageSource = FakeBlurImageSource::DesktopWallpaper; - } else { - fakeBlur.imageSource = FakeBlurImageSource::Custom; - } - fakeBlur.blurCustomImage = BlurConfig::fakeBlurCustomImageBlur(); -} - -} \ No newline at end of file diff --git a/src/settings.h b/src/settings.h deleted file mode 100644 index aa72cba940..0000000000 --- a/src/settings.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include - -namespace KWin -{ - -enum class FakeBlurImageSource -{ - Custom, - DesktopWallpaper -}; - -enum class WindowClassMatchingMode -{ - Blacklist, - Whitelist -}; - - -struct GeneralSettings -{ - int blurStrength; - int noiseStrength; - bool windowOpacityAffectsBlur; -}; - -struct ForceBlurSettings -{ - QStringList windowClasses; - WindowClassMatchingMode windowClassMatchingMode; - bool blurDecorations; - bool blurMenus; - bool blurDocks; - bool markWindowAsTranslucent; -}; - -struct RoundedCornersSettings -{ - float windowTopRadius; - float windowBottomRadius; - float menuRadius; - float dockRadius; - float antialiasing; - bool roundMaximized; -}; - -struct FakeBlurSettings -{ - bool enable; - bool disableWhenWindowBehind; - FakeBlurImageSource imageSource; - QImage customImage; - bool blurCustomImage; -}; - -class BlurSettings -{ -public: - GeneralSettings general{}; - ForceBlurSettings forceBlur{}; - RoundedCornersSettings roundedCorners{}; - FakeBlurSettings fakeBlur{}; - - void read(); -}; - -} diff --git a/src/utils.h b/src/utils.h index b6ca4375f6..b578dcf54a 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,12 +1,9 @@ #pragma once -namespace KWin -{ +#include "effect/effectwindow.h" -inline bool isMenu(const EffectWindow *w) +namespace KWin { - return w->isMenu() || w->isDropdownMenu() || w->isPopupMenu() || w->isPopupWindow(); -} inline bool isDockFloating(const EffectWindow *dock, const QRegion blurRegion) { diff --git a/src/windowrules/windowproperties.cpp b/src/windowrules/windowproperties.cpp new file mode 100644 index 0000000000..e33a282f29 --- /dev/null +++ b/src/windowrules/windowproperties.cpp @@ -0,0 +1,46 @@ +#include "windowproperties.h" + +namespace BetterBlur +{ + +void WindowProperties::setWindowOpacityAffectsBlur(const bool &windowOpacityAffectsBlur) +{ + m_windowOpacityAffectsBlur = windowOpacityAffectsBlur; +}; + +void WindowProperties::setBlurContent(const bool &blurContent) +{ + m_blurContent = blurContent; +} + +void WindowProperties::setBlurDecorations(const bool &blurDecorations) +{ + m_blurDecorations = blurDecorations; +} + +void WindowProperties::setForceTransparency(const bool &forceTransparency) +{ + m_forceTransparency = forceTransparency; +} + +void WindowProperties::setBottomCornerRadius(const float &radius) +{ + m_bottomCornerRadius = radius; +} + +void WindowProperties::setTopCornerRadius(const float &radius) +{ + m_topCornerRadius = radius; +} + +void WindowProperties::setCornerAntialiasing(const float &antialiasing) +{ + m_cornerAntialiasing = antialiasing; +} + +void WindowProperties::setStaticBlur(const bool &staticBlur) +{ + m_staticBlur = staticBlur; +} + +} \ No newline at end of file diff --git a/src/windowrules/windowproperties.h b/src/windowrules/windowproperties.h new file mode 100644 index 0000000000..a92264b285 --- /dev/null +++ b/src/windowrules/windowproperties.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include + +namespace BetterBlur +{ + +class WindowProperties +{ + Q_GADGET + + Q_PROPERTY(bool blurContent READ blurContent WRITE setBlurContent) + Q_PROPERTY(bool blurDecorations READ blurDecorations WRITE setBlurDecorations) + Q_PROPERTY(bool forceTransparency READ forceTransparency WRITE setForceTransparency) + + Q_PROPERTY(float bottomCornerRadius READ bottomCornerRadius WRITE setBottomCornerRadius) + Q_PROPERTY(float topCornerRadius READ topCornerRadius WRITE setTopCornerRadius) + Q_PROPERTY(float cornerAntialiasing READ cornerAntialiasing WRITE setCornerAntialiasing) +public: + bool windowOpacityAffectsBlur() const + { + return m_windowOpacityAffectsBlur.value_or(false); + } + void setWindowOpacityAffectsBlur(const bool &windowOpacityAffectsBlur); + + bool blurContent() const + { + return m_blurContent.value_or(false); + }; + void setBlurContent(const bool &blurContent); + + bool blurDecorations() const + { + return m_blurDecorations.value_or(false); + }; + void setBlurDecorations(const bool &blurDecorations); + + bool forceTransparency() const + { + return m_forceTransparency.value_or(false); + }; + void setForceTransparency(const bool &forceTransparency); + + float bottomCornerRadius() const + { + return m_bottomCornerRadius.value_or(0); + }; + void setBottomCornerRadius(const float &radius); + + float topCornerRadius() const + { + return m_topCornerRadius.value_or(0); + }; + void setTopCornerRadius(const float &radius); + + float cornerAntialiasing() const + { + return m_cornerAntialiasing.value_or(0); + }; + void setCornerAntialiasing(const float &antialiasing); + + bool staticBlur() const + { + return m_staticBlur.value_or(false); + } + void setStaticBlur(const bool &staticBlur); + +private: + std::optional m_windowOpacityAffectsBlur; + + std::optional m_blurContent; + std::optional m_blurDecorations; + std::optional m_forceTransparency; + + std::optional m_bottomCornerRadius; + std::optional m_topCornerRadius; + std::optional m_cornerAntialiasing; + + std::optional m_staticBlur; + + friend class Window; +}; + +} \ No newline at end of file diff --git a/src/windowrules/windowrule.cpp b/src/windowrules/windowrule.cpp new file mode 100644 index 0000000000..3b1435fcc8 --- /dev/null +++ b/src/windowrules/windowrule.cpp @@ -0,0 +1,21 @@ +#include "windowrule.h" + +namespace BetterBlur +{ + +WindowRule::WindowRule(const int &priority) + : m_priority(priority) +{ +} + +#ifdef CONFIG_KWIN +bool WindowRule::matches(const Window *w) const +{ + return m_conditions.empty() + || std::find_if(m_conditions.cbegin(), m_conditions.cend(), [w](const std::unique_ptr &condition) { + return condition->isSatisfied(w); + }) != m_conditions.cend(); +} +#endif + +} \ No newline at end of file diff --git a/src/windowrules/windowrule.h b/src/windowrules/windowrule.h new file mode 100644 index 0000000000..1b78e03b47 --- /dev/null +++ b/src/windowrules/windowrule.h @@ -0,0 +1,49 @@ +#pragma once + +#include "windowrulecondition.h" +#include "windowproperties.h" + +#ifdef CONFIG_KWIN +#include "blurwindow.h" +#endif + +#include + +namespace BetterBlur +{ + +#ifdef CONFIG_KWIN +class Window; +#endif +class WindowRuleCondition; + +class WindowRule +{ +public: + explicit WindowRule(const int &priority); + +#ifdef CONFIG_KWIN + bool matches(const Window *w) const; +#endif + + const int &priority() const + { + return m_priority; + } + + std::vector> &conditions() + { + return m_conditions; + } + + WindowProperties *properties() + { + return m_properties.get(); + } +private: + const int m_priority; + std::unique_ptr m_properties = std::make_unique(); + std::vector> m_conditions; +}; + +} \ No newline at end of file diff --git a/src/windowrules/windowrulecondition.cpp b/src/windowrules/windowrulecondition.cpp new file mode 100644 index 0000000000..7282055803 --- /dev/null +++ b/src/windowrules/windowrulecondition.cpp @@ -0,0 +1,107 @@ +#include "windowrulecondition.h" + +#ifdef CONFIG_KWIN +#include "blur.h" +#include "window.h" +#endif + +namespace BetterBlur +{ + +#ifdef CONFIG_KWIN +bool WindowRuleCondition::isSatisfied(const Window *w) const +{ + return isHasWindowBehindSubConditionSatisfied(w) + && isWindowClassSubConditionSatisfied(w) + && isWindowStateSubConditionSatisfied(w) + && isWindowTypeSubConditionSatisfied(w); +} + +bool WindowRuleCondition::isHasWindowBehindSubConditionSatisfied(const Window *w) const +{ + if (!m_hasWindowBehind.has_value()) { + return true; + } + + return w->hasWindowBehind() == *m_hasWindowBehind; +} + +bool WindowRuleCondition::isWindowClassSubConditionSatisfied(const Window *w) const +{ + if (!m_windowClass) { + return true; + } + + return ((*m_windowClass).match(w->window()->window()->resourceClass()).hasMatch() + || (*m_windowClass).match(w->window()->window()->resourceName()).hasMatch()) + == !m_negateWindowClasses; +} + +bool WindowRuleCondition::isWindowStateSubConditionSatisfied(const Window *w) const +{ + if (m_windowStates == WindowState::All) { + return true; + } + + const auto effectWindow = w->window(); + const bool satisfied = + ((m_windowStates & WindowState::Fullscreen) && effectWindow->isFullScreen()) + || ((m_windowStates & WindowState::Maximized) && w->isMaximized()); + return m_negateWindowStates == !satisfied; +} + +bool WindowRuleCondition::isWindowTypeSubConditionSatisfied(const Window *w) const +{ + if (m_windowTypes == WindowType::All) + return true; + + const auto effectWindow = w->window(); + const bool satisfied = + ((m_windowTypes & WindowType::Normal) && effectWindow->isNormalWindow()) + || ((m_windowTypes & WindowType::Dock) && effectWindow->isDock()) + || ((m_windowTypes & WindowType::Toolbar) && effectWindow->isToolbar()) + || ((m_windowTypes & WindowType::Menu) + && (effectWindow->isMenu() || effectWindow->isDropdownMenu() || effectWindow->isPopupMenu() || effectWindow->isPopupWindow())) + || ((m_windowTypes & WindowType::Dialog) && effectWindow->isDialog()) + || ((m_windowTypes & WindowType::Utility) && effectWindow->isUtility()) + || ((m_windowTypes & WindowType::Tooltip) && effectWindow->isTooltip()); + return m_negateWindowTypes == !satisfied; +} +#endif + +void WindowRuleCondition::setNegateWindowClasses(const bool &negate) +{ + m_negateWindowClasses = negate; +} + +void WindowRuleCondition::setNegateWindowStates(const bool &negate) +{ + m_negateWindowStates = negate; +} + +void WindowRuleCondition::setNegateWindowTypes(const bool &negate) +{ + m_negateWindowTypes = negate; +} + +void WindowRuleCondition::setHasWindowBehind(const bool &hasWindowBehind) +{ + m_hasWindowBehind = hasWindowBehind; +} + +void WindowRuleCondition::setWindowClass(const QRegularExpression &windowClass) +{ + m_windowClass = windowClass; +} + +void WindowRuleCondition::setWindowStates(const WindowStates &windowStates) +{ + m_windowStates = windowStates; +} + +void WindowRuleCondition::setWindowTypes(const WindowTypes &windowTypes) +{ + m_windowTypes = windowTypes; +} + +} \ No newline at end of file diff --git a/src/windowrules/windowrulecondition.h b/src/windowrules/windowrulecondition.h new file mode 100644 index 0000000000..76276d4543 --- /dev/null +++ b/src/windowrules/windowrulecondition.h @@ -0,0 +1,105 @@ +#pragma once + +#ifdef CONFIG_KWIN +#include "blurwindow.h" +#endif + +#include + +#include + +namespace BetterBlur +{ + +#ifdef CONFIG_KWIN +class Window; +#endif + +enum class WindowClassMatchingMode +{ + Exact, + Substring, + Regex +}; + +enum class WindowState : uint32_t +{ + Maximized = 1u << 0, + Fullscreen = 1u << 1, + All = 0U - 1, +}; +Q_DECLARE_FLAGS(WindowStates, WindowState) +Q_DECLARE_OPERATORS_FOR_FLAGS(WindowStates) + +enum class WindowType : uint32_t +{ + Normal = 1u << 0, + Dock = 1u << 1, + Toolbar = 1u << 2, + Menu = 1u << 3, + Dialog = 1u << 4, + Utility = 1u << 5, + Tooltip = 1u << 6, + All = 0U - 1, +}; +Q_DECLARE_FLAGS(WindowTypes, WindowType) + +class WindowRuleCondition +{ + Q_GADGET + +public: +#ifdef CONFIG_KWIN + bool isSatisfied(const Window *w) const; +#endif + + void setNegateWindowClasses(const bool &negate); + void setNegateWindowStates(const bool &negate); + void setNegateWindowTypes(const bool &negate); + + void setHasWindowBehind(const bool &hasWindowBehind); + void setWindowClass(const QRegularExpression &windowClass); + void setWindowStates(const WindowStates &windowStates); + void setWindowTypes(const WindowTypes &windowTypes); + + std::optional &windowClass() + { + return m_windowClass; + } + + const WindowStates &windowStates() const + { + return m_windowStates; + } + + const WindowTypes &windowTypes() const + { + return m_windowTypes; + } +private: +#ifdef CONFIG_KWIN + bool isHasWindowBehindSubConditionSatisfied(const Window *w) const; + bool isWindowClassSubConditionSatisfied(const Window *w) const; + bool isWindowStateSubConditionSatisfied(const Window *w) const; + bool isWindowTypeSubConditionSatisfied(const Window *w) const; +#endif + + std::optional m_hasWindowBehind; + + std::optional m_windowClass; + bool m_negateWindowClasses = false; + + /** + * Ignored if set to WindowState::All. + */ + WindowStates m_windowStates = WindowState::All; + bool m_negateWindowStates = false; + + /** + * Ignored if set to WindowType::All. + */ + WindowTypes m_windowTypes = WindowType::All; + bool m_negateWindowTypes = false; +}; + +} diff --git a/src/windowrules/windowrulelist.cpp b/src/windowrules/windowrulelist.cpp new file mode 100644 index 0000000000..d2ad746583 --- /dev/null +++ b/src/windowrules/windowrulelist.cpp @@ -0,0 +1,224 @@ +#include "windowrulelist.h" + +#include "config.h" + +#include + +namespace BetterBlur +{ + +void WindowRuleList::load() +{ + m_windowRules.clear(); + + if (Config::convertSimpleConfigToRules()) { + loadSimple(); + } + + KConfig config("kwinbetterblurrc", KConfig::SimpleConfig); + const auto windowRulesGroup = config.group("WindowRules"); + for (const auto &windowRuleGroupName : windowRulesGroup.groupList()) { + const auto windowRuleGroup = windowRulesGroup.group(windowRuleGroupName); + + const auto priority = windowRuleGroup.readEntry("Priority", 0); + auto rule = std::make_unique(priority); + auto ruleProperties = rule->properties(); + + auto conditionsGroup = windowRuleGroup.group("Conditions"); + for (const auto &conditionGroupName : conditionsGroup.groupList()) { + const auto conditionGroup = conditionsGroup.group(conditionGroupName); + auto condition = std::make_unique(); + + for (const auto &subcondition : conditionGroup.readEntry("Negate", "").split(" ")) { + if (subcondition == "WindowClass") { + condition->setNegateWindowClasses(true); + } else if (subcondition == "WindowState") { + condition->setNegateWindowStates(true); + } else if (subcondition == "WindowType") { + condition->setNegateWindowTypes(true); + } + } + + if (conditionGroup.hasKey("HasWindowBehind")) { + condition->setHasWindowBehind(conditionGroup.readEntry("HasWindowBehind", false)); + } + + if (conditionGroup.hasKey("WindowClass")) { + condition->setWindowClass(QRegularExpression(conditionGroup.readEntry("WindowClass", ""))); + } + + if (conditionGroup.hasKey("WindowState")) { + WindowStates states; + for (const auto &stateStr : conditionGroup.readEntry("WindowState", "").split(" ")) { + if (stateStr == "Fullscreen") { + states |= WindowState::Fullscreen; + } else if (stateStr == "Maximized") { + states |= WindowState::Maximized; + } + } + condition->setWindowStates(states); + } + + if (conditionGroup.hasKey("WindowType")) { + WindowTypes types; + for (const auto &typeStr : conditionGroup.readEntry("WindowType", "").split(" ")) { + if (typeStr == "Dialog") { + types |= WindowType::Dialog; + } else if (typeStr == "Dock") { + types |= WindowType::Dock; + } else if (typeStr == "Normal") { + types |= WindowType::Normal; + } else if (typeStr == "Menu") { + types |= WindowType::Menu; + } else if (typeStr == "Toolbar") { + types |= WindowType::Toolbar; + } else if (typeStr == "Tooltip") { + types |= WindowType::Tooltip; + } else if (typeStr == "Utility") { + types |= WindowType::Utility; + } + } + condition->setWindowTypes(types); + } + + rule->conditions().push_back(std::move(condition)); + } + + auto propertiesGroup = windowRuleGroup.group("Properties"); + if (propertiesGroup.hasKey("BlurContent")) { + ruleProperties->setBlurContent(propertiesGroup.readEntry("BlurContent", false)); + } + if (propertiesGroup.hasKey("BlurDecorations")) { + ruleProperties->setBlurDecorations(propertiesGroup.readEntry("BlurDecorations", false)); + } + if (propertiesGroup.hasKey("CornerRadius")) { + float topCornerRadius = 0; + float bottomCornerRadius = 0; + + auto cornerRadius = propertiesGroup.readEntry("CornerRadius", ""); + if (cornerRadius.contains(" ")) { + topCornerRadius = cornerRadius.split(" ")[0].toFloat(); + bottomCornerRadius = cornerRadius.split(" ")[1].toFloat(); + } else { + topCornerRadius = bottomCornerRadius = cornerRadius.toFloat(); + } + + if (topCornerRadius >= 0) { + ruleProperties->setTopCornerRadius(topCornerRadius); + } + if (bottomCornerRadius >= 0) { + ruleProperties->setBottomCornerRadius(bottomCornerRadius); + } + } + if (propertiesGroup.hasKey("CornerAntialiasing")) { + ruleProperties->setCornerAntialiasing(propertiesGroup.readEntry("CornerAntialiasing", 0.0)); + } + if (propertiesGroup.hasKey("ForceTransparency")) { + ruleProperties->setForceTransparency(propertiesGroup.readEntry("ForceTransparency", false)); + } + if (propertiesGroup.hasKey("StaticBlur")) { + ruleProperties->setStaticBlur(propertiesGroup.readEntry("StaticBlur", false)); + } + if (propertiesGroup.hasKey("WindowOpacityAffectsBlur")) { + ruleProperties->setWindowOpacityAffectsBlur(propertiesGroup.readEntry("WindowOpacityAffectsBlur", false)); + } + + if (m_windowRules.empty()) { + m_windowRules.push_back(std::move(rule)); + continue; + } + auto it = std::lower_bound(m_windowRules.begin(), m_windowRules.end(), rule, [](const std::unique_ptr& a, const std::unique_ptr& b) { + return a->priority() < b->priority(); + }); + m_windowRules.insert(it, std::move(rule)); + } +} + +void WindowRuleList::loadSimple() +{ + m_windowRules.clear(); + + KConfig kwinrc("kwinrc", KConfig::SimpleConfig); + auto betterBlurV1Group = kwinrc.group("Effect-blurplus"); + + // Force blur + auto forceBlurRule = std::make_unique(-1); + forceBlurRule->properties()->setBlurContent(true); + forceBlurRule->properties()->setBlurDecorations(betterBlurV1Group.readEntry("BlurDecorations", false)); + forceBlurRule->properties()->setForceTransparency(betterBlurV1Group.readEntry("PaintAsTranslucent", false)); + auto forceBlurRuleCondition = std::make_unique(); + forceBlurRuleCondition->setNegateWindowClasses(!betterBlurV1Group.readEntry("BlurMatching", true)); + QStringList regexes; + for (const auto &windowClass : betterBlurV1Group.readEntry("WindowClasses", "").split("\n")) { + regexes << (windowClass.startsWith("$regex:") + ? windowClass.mid(7) + : "^" + QRegularExpression::escape(windowClass) + "$"); + } + if (!regexes.empty()) { + forceBlurRuleCondition->setWindowClass(QRegularExpression(regexes.join("|"))); + } + WindowTypes types = WindowType::Normal; + if (betterBlurV1Group.readEntry("BlurMenus", false)) { + types |= WindowType::Menu; + } + if (betterBlurV1Group.readEntry("BlurDocks", false)) { + types |= WindowType::Dock; + } + forceBlurRuleCondition->setWindowTypes(types); + forceBlurRule->conditions().push_back(std::move(forceBlurRuleCondition)); + m_windowRules.push_back(std::move(forceBlurRule)); + + auto cornerAntialiasingRule = std::make_unique(-1); + cornerAntialiasingRule->properties()->setCornerAntialiasing(betterBlurV1Group.readEntry("RoundedCornersAntialiasing", static_cast(0.0))); + m_windowRules.push_back(std::move(cornerAntialiasingRule)); + + auto windowCornerRadiusRule = std::make_unique(-1); + windowCornerRadiusRule->properties()->setTopCornerRadius(betterBlurV1Group.readEntry("TopCornerRadius", static_cast(0.0))); + windowCornerRadiusRule->properties()->setBottomCornerRadius(betterBlurV1Group.readEntry("BottomCornerRadius", static_cast(0.0))); + if (!betterBlurV1Group.readEntry("RoundCornersOfMaximizedWindows", false)) { + auto dontRoundMaximizedWindowsCondition = std::make_unique(); + dontRoundMaximizedWindowsCondition->setWindowStates(WindowState::Fullscreen | WindowState::Maximized); + dontRoundMaximizedWindowsCondition->setNegateWindowStates(true); + windowCornerRadiusRule->conditions().push_back(std::move(dontRoundMaximizedWindowsCondition)); + } + m_windowRules.push_back(std::move(windowCornerRadiusRule)); + + // Menu corner radius + auto roundedCornersRule3 = std::make_unique(-1); + auto radius = betterBlurV1Group.readEntry("MenuCornerRadius", static_cast(0.0)); + roundedCornersRule3->properties()->setTopCornerRadius(radius); + roundedCornersRule3->properties()->setBottomCornerRadius(radius); + auto roundedCornersRule3Condition = std::make_unique(); + roundedCornersRule3Condition->setWindowTypes(WindowType::Menu); + roundedCornersRule3->conditions().push_back(std::move(roundedCornersRule3Condition)); + m_windowRules.push_back(std::move(roundedCornersRule3)); + + // Dock corner radius + auto roundedCornersRule4 = std::make_unique(-1); + radius = betterBlurV1Group.readEntry("DockCornerRadius", static_cast(0.0)); + roundedCornersRule4->properties()->setTopCornerRadius(radius); + roundedCornersRule4->properties()->setBottomCornerRadius(radius); + auto roundedCornersRule4Condition = std::make_unique(); + roundedCornersRule4Condition->setWindowTypes(WindowType::Dock); + roundedCornersRule4->conditions().push_back(std::move(roundedCornersRule4Condition)); + m_windowRules.push_back(std::move(roundedCornersRule4)); + + if (betterBlurV1Group.readEntry("FakeBlur", false)) { + auto staticBlurRule = std::make_unique(-1); + staticBlurRule->properties()->setStaticBlur(true); + if (betterBlurV1Group.readEntry("FakeBlurDisableWhenWindowBehind", true)) { + auto condition = std::make_unique(); + condition->setHasWindowBehind(false); + staticBlurRule->conditions().push_back(std::move(condition)); + } + m_windowRules.push_back(std::move(staticBlurRule)); + } + + if (betterBlurV1Group.readEntry("TransparentBlur", true)) { + auto blurOpacityRule = std::make_unique(-1); + blurOpacityRule->properties()->setWindowOpacityAffectsBlur(true); + m_windowRules.push_back(std::move(blurOpacityRule)); + } +} + +} \ No newline at end of file diff --git a/src/windowrules/windowrulelist.h b/src/windowrules/windowrulelist.h new file mode 100644 index 0000000000..5c58b10ab1 --- /dev/null +++ b/src/windowrules/windowrulelist.h @@ -0,0 +1,32 @@ +#pragma once + +#include "windowrule.h" + +#include +#include + +namespace BetterBlur +{ + +class WindowRule; + +class WindowRuleList +{ +public: + void load(); + + std::vector> &rules() + { + return m_windowRules; + } + const std::vector> &rules() const + { + return m_windowRules; + } +private: + void loadSimple(); + + std::vector> m_windowRules; +}; + +} \ No newline at end of file From 70e5190d51db1bcb39afeb81711855be65c514d2 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Sun, 24 Nov 2024 16:58:02 +0100 Subject: [PATCH 02/19] blurwindow: fix content blur region --- src/blurwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blurwindow.cpp b/src/blurwindow.cpp index a26b6ad608..1286f19134 100644 --- a/src/blurwindow.cpp +++ b/src/blurwindow.cpp @@ -97,7 +97,7 @@ void Window::updateBlurRegion(bool geometryChanged) // Don't override blur region for menus that already have one. The window geometry could include shadows. if (!((isMenu() || w->isTooltip()) && (content.has_value() || geometryChanged))) { if (m_properties->blurContent()) { - content = w->contentsRect().toRect(); + content = w->contentsRect().translated(-w->contentsRect().topLeft()).toRect(); } if (m_properties->blurDecorations() && w->decoration()) { frame = QRegion(w->frameGeometry().translated(-w->x(), -w->y()).toRect()) - w->contentsRect().toRect(); From 38d1e58102a7c828d74c876c63dd3e24a3f60d1e Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Sun, 24 Nov 2024 17:42:31 +0100 Subject: [PATCH 03/19] windowrules/condition: add internal subcondition --- docs/configuration.md | 1 + src/blur.cpp | 1 + src/windowrules/windowrulecondition.cpp | 17 +++++++++++++++++ src/windowrules/windowrulecondition.h | 4 +++- src/windowrules/windowrulelist.cpp | 4 ++++ 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 2362ef5ae1..9682b80588 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -68,6 +68,7 @@ At least 1 (or 0 if none specified) condition must be satisfied in order for thi |-----------------------------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Negate** | ``enum (WindowClass, WindowState, WindowType)``
Multiple values are allowed. | Which subconditions should be negated.

Separated by space. | | **HasWindowBehind** | ``bool`` | Whether only windows that (don't) have another window (excluding desktops) behind them should satisfy this condition. | +| **Internal** | ``bool`` | Internal KWin windows. | | **WindowClass** | ``string`` or ``regex`` | A regular expression executed on the window class. Only a partial match is required.

For an exact match, use ``^class$``.
To specify multiple classes, separate them by ``\|``: ``class1\|class2``.
A dot matches any character, which can be prevented by escaping it: ``\.``. | - | | **WindowState** | ``enum (Fullscreen, Maximized)``
Multiple values are allowed. | Separated by space. | | **WindowType** | ``enum (Dialog, Dock, Normal, Menu, Toolbar, Tooltip, Utility)``
Multiple values are allowed. | Separated by space. diff --git a/src/blur.cpp b/src/blur.cpp index 39de257429..5ed012f232 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -14,6 +14,7 @@ #include "core/rendertarget.h" #include "core/renderviewport.h" #include "effect/effecthandler.h" +#include "internalwindow.h" #include "opengl/glutils.h" #include "opengl/glplatform.h" #include "scene/windowitem.h" diff --git a/src/windowrules/windowrulecondition.cpp b/src/windowrules/windowrulecondition.cpp index 7282055803..c93d7438e6 100644 --- a/src/windowrules/windowrulecondition.cpp +++ b/src/windowrules/windowrulecondition.cpp @@ -2,6 +2,8 @@ #ifdef CONFIG_KWIN #include "blur.h" + +#include "internalwindow.h" #include "window.h" #endif @@ -12,6 +14,7 @@ namespace BetterBlur bool WindowRuleCondition::isSatisfied(const Window *w) const { return isHasWindowBehindSubConditionSatisfied(w) + && isInternalSubConditionSatisfied(w) && isWindowClassSubConditionSatisfied(w) && isWindowStateSubConditionSatisfied(w) && isWindowTypeSubConditionSatisfied(w); @@ -26,6 +29,15 @@ bool WindowRuleCondition::isHasWindowBehindSubConditionSatisfied(const Window *w return w->hasWindowBehind() == *m_hasWindowBehind; } +bool WindowRuleCondition::isInternalSubConditionSatisfied(const Window *w) const +{ + if (!m_internal.has_value()) { + return true; + } + + return static_cast(qobject_cast(w->window()->window())) == *m_internal; +} + bool WindowRuleCondition::isWindowClassSubConditionSatisfied(const Window *w) const { if (!m_windowClass) { @@ -89,6 +101,11 @@ void WindowRuleCondition::setHasWindowBehind(const bool &hasWindowBehind) m_hasWindowBehind = hasWindowBehind; } +void WindowRuleCondition::setInternal(const bool &internal) +{ + m_internal = internal; +} + void WindowRuleCondition::setWindowClass(const QRegularExpression &windowClass) { m_windowClass = windowClass; diff --git a/src/windowrules/windowrulecondition.h b/src/windowrules/windowrulecondition.h index 76276d4543..878cb180e2 100644 --- a/src/windowrules/windowrulecondition.h +++ b/src/windowrules/windowrulecondition.h @@ -58,6 +58,7 @@ class WindowRuleCondition void setNegateWindowTypes(const bool &negate); void setHasWindowBehind(const bool &hasWindowBehind); + void setInternal(const bool &internal); void setWindowClass(const QRegularExpression &windowClass); void setWindowStates(const WindowStates &windowStates); void setWindowTypes(const WindowTypes &windowTypes); @@ -79,12 +80,13 @@ class WindowRuleCondition private: #ifdef CONFIG_KWIN bool isHasWindowBehindSubConditionSatisfied(const Window *w) const; + bool isInternalSubConditionSatisfied(const Window *w) const; bool isWindowClassSubConditionSatisfied(const Window *w) const; bool isWindowStateSubConditionSatisfied(const Window *w) const; bool isWindowTypeSubConditionSatisfied(const Window *w) const; #endif - std::optional m_hasWindowBehind; + std::optional m_internal; std::optional m_windowClass; bool m_negateWindowClasses = false; diff --git a/src/windowrules/windowrulelist.cpp b/src/windowrules/windowrulelist.cpp index d2ad746583..75fa1e8c19 100644 --- a/src/windowrules/windowrulelist.cpp +++ b/src/windowrules/windowrulelist.cpp @@ -43,6 +43,10 @@ void WindowRuleList::load() condition->setHasWindowBehind(conditionGroup.readEntry("HasWindowBehind", false)); } + if (conditionGroup.hasKey("Internal")) { + condition->setInternal(conditionGroup.readEntry("Internal", false)); + } + if (conditionGroup.hasKey("WindowClass")) { condition->setWindowClass(QRegularExpression(conditionGroup.readEntry("WindowClass", ""))); } From c74c1c9c83f0945f568b8e03805c9163924b31ea Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Mon, 25 Nov 2024 12:28:25 +0100 Subject: [PATCH 04/19] windowrules/properties: add blur strength --- docs/configuration.md | 23 ++++++++++++----------- src/blur.cpp | 20 +++++++++++--------- src/blur.h | 3 --- src/blurwindow.cpp | 7 ++++++- src/windowrules/windowproperties.cpp | 5 +++++ src/windowrules/windowproperties.h | 7 +++++++ src/windowrules/windowrulelist.cpp | 7 +++++++ 7 files changed, 48 insertions(+), 24 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 9682b80588..0335a2a9bb 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -69,24 +69,25 @@ At least 1 (or 0 if none specified) condition must be satisfied in order for thi | **Negate** | ``enum (WindowClass, WindowState, WindowType)``
Multiple values are allowed. | Which subconditions should be negated.

Separated by space. | | **HasWindowBehind** | ``bool`` | Whether only windows that (don't) have another window (excluding desktops) behind them should satisfy this condition. | | **Internal** | ``bool`` | Internal KWin windows. | -| **WindowClass** | ``string`` or ``regex`` | A regular expression executed on the window class. Only a partial match is required.

For an exact match, use ``^class$``.
To specify multiple classes, separate them by ``\|``: ``class1\|class2``.
A dot matches any character, which can be prevented by escaping it: ``\.``. | - | +| **WindowClass** | ``string`` or ``regex`` | A regular expression executed on the window class. Only a partial match is required.

For an exact match, use ``^class$``.
To specify multiple classes, separate them by ``\|``: ``class1\|class2``.
A dot matches any character, which can be prevented by escaping it: ``\.``. | | **WindowState** | ``enum (Fullscreen, Maximized)``
Multiple values are allowed. | Separated by space. | -| **WindowType** | ``enum (Dialog, Dock, Normal, Menu, Toolbar, Tooltip, Utility)``
Multiple values are allowed. | Separated by space. +| **WindowType** | ``enum (Dialog, Dock, Normal, Menu, Toolbar, Tooltip, Utility)``
Multiple values are allowed. | Separated by space. | ### Properties **Group**: [WindowRules][\$RuleName][Properties] Properties don't have default values. Unset properties don't override properties of other rules. -| Property | Type | Description | -|:-----------------------------|------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **BlurContent** | ``bool`` | Whether to force blur the window's content, excluding decorations. | -| **BlurDecorations** | ``bool`` | See *Blur window decorations as well* in the *Simple* section. The difference is that in window rules it's possible to blur only the decorations and the corner radius is specified in the *CornerRadius* property. | -| **CornerRadius** | ``float (>= 0)`` | Corner radius for the blur region.

A single number specifies the same radius for both top and bottom corners. Example: ``12.5``
Two numbers separated by a space specify different radii for top and bottom corners respectively. Example: ``0 12.5``

Top corner radius will only be applied to windows with force-blurred decorations or no decorations. | -| **CornerAntialiasing** | ``float (>= 0)`` | Makes rounded corners appear smoother. Recommended value is 1. | -| **ForceTransparency** | ``bool`` | See *Treat windows as transparent* in the *Simple* section. | -| **StaticBlur** | ``bool`` | Whether to use static blur instead of dynamic blur for the window. -| **WindowOpacityAffectsBlur** | ``bool`` | See *Window opacity affects blur* in the *Simple* section. | +| Property | Type | Description | +|:-----------------------------|------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **BlurContent** | ``bool`` | Whether to force blur the window's content, excluding decorations. | +| **BlurDecorations** | ``bool`` | See *Blur window decorations as well* in the *Simple* section. The difference is that in window rules it's possible to blur only the decorations and the corner radius is specified in the *CornerRadius* property. | +| **BlurStrength** | ``int (1 - 15)`` | Currently doesn't affect static blur, which still uses the global blur strength. | +| **CornerRadius** | ``float (>= 0)`` | Corner radius for the blur region.

A single number specifies the same radius for both top and bottom corners. Example: ``12.5``
Two numbers separated by a space specify different radii for top and bottom corners respectively. Example: ``0 12.5``

Top corner radius will only be applied to windows with force-blurred decorations or no decorations. | +| **CornerAntialiasing** | ``float (>= 0)`` | Makes rounded corners appear smoother. Recommended value is 1. | +| **ForceTransparency** | ``bool`` | See *Treat windows as transparent* in the *Simple* section. | +| **StaticBlur** | ``bool`` | Whether to use static blur instead of dynamic blur for the window. | +| **WindowOpacityAffectsBlur** | ``bool`` | See *Window opacity affects blur* in the *Simple* section. | ## Example ``` diff --git a/src/blur.cpp b/src/blur.cpp index 5ed012f232..9243db88de 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -237,10 +237,6 @@ void BlurEffect::reconfigure(ReconfigureFlags flags) BetterBlur::Config::self()->read(); m_config->load(); - int blurStrength = BetterBlur::Config::blurStrength() - 1; - m_iterationCount = blurStrengthValues[blurStrength].iteration; - m_offset = blurStrengthValues[blurStrength].offset; - m_expandSize = blurOffsets[m_iterationCount - 1].expandSize; m_noiseStrength = BetterBlur::Config::noiseStrength(); m_staticBlurImage = QImage(BetterBlur::Config::fakeBlurImage()); m_staticBlurTextures.clear(); @@ -407,8 +403,10 @@ void BlurEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std:: if (data.opaque.intersects(m_currentBlur)) { // to blur an area partially we have to shrink the opaque area of a window QRegion newOpaque; + auto iterationCount = blurStrengthValues[blurWindow->properties()->blurStrength() - 1].iteration; + auto expandSize = blurOffsets[iterationCount - 1].expandSize; for (const QRect &rect : data.opaque) { - newOpaque += rect.adjusted(m_expandSize, m_expandSize, -m_expandSize, -m_expandSize); + newOpaque += rect.adjusted(expandSize, expandSize, -expandSize, -expandSize); } data.opaque = newOpaque; @@ -586,14 +584,18 @@ void BlurEffect::blur(BetterBlur::Window *w, BetterBlur::BlurRenderData &renderI staticBlurTexture = ensureStaticBlurTexture(m_currentScreen, renderTarget); } + auto blurStrength = (w ? w->properties()->blurStrength() : BetterBlur::Config::blurStrength()) - 1; + size_t iterationCount = blurStrengthValues[blurStrength].iteration; + auto offset = blurStrengthValues[blurStrength].offset; + if (!staticBlurTexture - && (renderInfo.framebuffers.size() != (m_iterationCount + 1) + && (renderInfo.framebuffers.size() != (iterationCount + 1) || renderInfo.textures[0]->size() != backgroundRect.size() || renderInfo.textures[0]->internalFormat() != textureFormat)) { renderInfo.framebuffers.clear(); renderInfo.textures.clear(); - for (size_t i = 0; i <= m_iterationCount; ++i) { + for (size_t i = 0; i <= iterationCount; ++i) { auto texture = GLTexture::allocate(textureFormat, backgroundRect.size() / (1 << i)); if (!texture) { qCWarning(KWIN_BLUR) << "Failed to allocate an offscreen texture"; @@ -764,7 +766,7 @@ void BlurEffect::blur(BetterBlur::Window *w, BetterBlur::BlurRenderData &renderI projectionMatrix.ortho(QRectF(0.0, 0.0, backgroundRect.width(), backgroundRect.height())); m_downsamplePass.shader->setUniform(m_downsamplePass.mvpMatrixLocation, projectionMatrix); - m_downsamplePass.shader->setUniform(m_downsamplePass.offsetLocation, float(m_offset)); + m_downsamplePass.shader->setUniform(m_downsamplePass.offsetLocation, offset); for (size_t i = 1; i < renderInfo.framebuffers.size(); ++i) { const auto &read = renderInfo.framebuffers[i - 1]; @@ -793,7 +795,7 @@ void BlurEffect::blur(BetterBlur::Window *w, BetterBlur::BlurRenderData &renderI m_upsamplePass.shader->setUniform(m_upsamplePass.bottomCornerRadiusLocation, static_cast(0)); m_upsamplePass.shader->setUniform(m_upsamplePass.mvpMatrixLocation, projectionMatrix); m_upsamplePass.shader->setUniform(m_upsamplePass.noiseLocation, false); - m_upsamplePass.shader->setUniform(m_upsamplePass.offsetLocation, float(m_offset)); + m_upsamplePass.shader->setUniform(m_upsamplePass.offsetLocation, offset); for (size_t i = renderInfo.framebuffers.size() - 1; i > 1; --i) { GLFramebuffer::popFramebuffer(); diff --git a/src/blur.h b/src/blur.h index 058c62ec90..1d618b5079 100644 --- a/src/blur.h +++ b/src/blur.h @@ -149,9 +149,6 @@ public Q_SLOTS: Output *m_currentScreen = nullptr; QRegion m_windowGeometriesSum; - size_t m_iterationCount; // number of times the texture will be downsized to half size - int m_offset; - int m_expandSize; int m_noiseStrength; std::unique_ptr noiseTexture; diff --git a/src/blurwindow.cpp b/src/blurwindow.cpp index 1286f19134..8310f7fc4d 100644 --- a/src/blurwindow.cpp +++ b/src/blurwindow.cpp @@ -209,8 +209,10 @@ void Window::updateProperties() // TODO Rule evaluation needs to be optimized. Sometimes it's not necessary to evaluate a rule. auto staticBlur = false; + uint8_t blurStrength = 0; if (m_properties) { staticBlur = m_properties->staticBlur(); + blurStrength = m_properties->blurStrength(); } m_properties = std::make_unique(); @@ -223,6 +225,9 @@ void Window::updateProperties() if (ruleProperties->m_blurContent) { m_properties->setBlurContent(*ruleProperties->m_blurContent); } + if (ruleProperties->m_blurStrength) { + m_properties->setBlurStrength(*ruleProperties->m_blurStrength); + } if (ruleProperties->m_blurDecorations) { m_properties->setBlurDecorations(*ruleProperties->m_blurDecorations); } @@ -246,7 +251,7 @@ void Window::updateProperties() } } - if (staticBlur != m_properties->staticBlur()) { + if (staticBlur != m_properties->staticBlur() || blurStrength != m_properties->blurStrength()) { w->addRepaintFull(); } } diff --git a/src/windowrules/windowproperties.cpp b/src/windowrules/windowproperties.cpp index e33a282f29..52b11e2e60 100644 --- a/src/windowrules/windowproperties.cpp +++ b/src/windowrules/windowproperties.cpp @@ -3,6 +3,11 @@ namespace BetterBlur { +void WindowProperties::setBlurStrength(const uint8_t &blurStrength) +{ + m_blurStrength = blurStrength; +} + void WindowProperties::setWindowOpacityAffectsBlur(const bool &windowOpacityAffectsBlur) { m_windowOpacityAffectsBlur = windowOpacityAffectsBlur; diff --git a/src/windowrules/windowproperties.h b/src/windowrules/windowproperties.h index a92264b285..8db72fbc1e 100644 --- a/src/windowrules/windowproperties.h +++ b/src/windowrules/windowproperties.h @@ -19,6 +19,12 @@ class WindowProperties Q_PROPERTY(float topCornerRadius READ topCornerRadius WRITE setTopCornerRadius) Q_PROPERTY(float cornerAntialiasing READ cornerAntialiasing WRITE setCornerAntialiasing) public: + uint8_t blurStrength() const + { + return m_blurStrength.value_or(1); + } + void setBlurStrength(const uint8_t &blurStrength); + bool windowOpacityAffectsBlur() const { return m_windowOpacityAffectsBlur.value_or(false); @@ -68,6 +74,7 @@ class WindowProperties void setStaticBlur(const bool &staticBlur); private: + std::optional m_blurStrength; std::optional m_windowOpacityAffectsBlur; std::optional m_blurContent; diff --git a/src/windowrules/windowrulelist.cpp b/src/windowrules/windowrulelist.cpp index 75fa1e8c19..d148f977b4 100644 --- a/src/windowrules/windowrulelist.cpp +++ b/src/windowrules/windowrulelist.cpp @@ -95,6 +95,9 @@ void WindowRuleList::load() if (propertiesGroup.hasKey("BlurDecorations")) { ruleProperties->setBlurDecorations(propertiesGroup.readEntry("BlurDecorations", false)); } + if (propertiesGroup.hasKey("BlurStrength")) { + ruleProperties->setBlurStrength(propertiesGroup.readEntry("BlurStrength", 15)); + } if (propertiesGroup.hasKey("CornerRadius")) { float topCornerRadius = 0; float bottomCornerRadius = 0; @@ -145,6 +148,10 @@ void WindowRuleList::loadSimple() KConfig kwinrc("kwinrc", KConfig::SimpleConfig); auto betterBlurV1Group = kwinrc.group("Effect-blurplus"); + auto blurStrengthRule = std::make_unique(-1); + blurStrengthRule->properties()->setBlurStrength(betterBlurV1Group.readEntry("BlurStrength", 15)); + m_windowRules.push_back(std::move(blurStrengthRule)); + // Force blur auto forceBlurRule = std::make_unique(-1); forceBlurRule->properties()->setBlurContent(true); From a3c217b95733211f24bd9615127fabfe21b5eec3 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:13:10 +0100 Subject: [PATCH 05/19] blur: fix build on plasma < 6.2 --- src/blur.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/blur.cpp b/src/blur.cpp index 0525717121..2cd05349af 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -17,7 +17,6 @@ #include "internalwindow.h" #include "opengl/glutils.h" #include "opengl/glplatform.h" -#include "scene/windowitem.h" #include "utils.h" #include "utils/xcbutils.h" #include "wayland/blur.h" @@ -365,7 +364,7 @@ void BlurEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std:: const auto hadWindowBehind = blurWindow->hasWindowBehind(); blurWindow->setHasWindowBehind(m_windowGeometriesSum.intersects(w->frameGeometry().toRect())); if (hadWindowBehind != blurWindow->hasWindowBehind()) { - data.paint += w->windowItem()->boundingRect().toRect(); + data.paint += w->expandedGeometry().toRect(); } } @@ -831,7 +830,7 @@ void BlurEffect::blur(BetterBlur::Window *w, BetterBlur::BlurRenderData &renderI m_upsamplePass.shader->setUniform(m_upsamplePass.topCornerRadiusLocation, topCornerRadius); m_upsamplePass.shader->setUniform(m_upsamplePass.bottomCornerRadiusLocation, bottomCornerRadius); - m_upsamplePass.shader->setUniform(m_upsamplePass.antialiasingLocation, w ? w->properties()->cornerAntialiasing() : 0.0); + m_upsamplePass.shader->setUniform(m_upsamplePass.antialiasingLocation, static_cast(w ? w->properties()->cornerAntialiasing() : 0.0)); m_upsamplePass.shader->setUniform(m_upsamplePass.blurSizeLocation, QVector2D(backgroundRect.width(), backgroundRect.height())); m_upsamplePass.shader->setUniform(m_upsamplePass.opacityLocation, static_cast(opacity)); From 134afb1efda5edc5b693eb81c192bdcf81aaf699 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:26:13 +0100 Subject: [PATCH 06/19] blurwindow: fix build on plasma 6.0.5 --- src/blurwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/blurwindow.cpp b/src/blurwindow.cpp index 8310f7fc4d..520a8d98ca 100644 --- a/src/blurwindow.cpp +++ b/src/blurwindow.cpp @@ -14,6 +14,8 @@ #include +#include + namespace BetterBlur { From ed0728e15bb550c6de9129404e86a2f4e6d4ee03 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:33:26 +0100 Subject: [PATCH 07/19] fix warnings --- src/blur.cpp | 2 +- src/blurwindow.cpp | 6 +++--- src/blurwindow.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/blur.cpp b/src/blur.cpp index 2cd05349af..a4d66068d0 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -252,7 +252,7 @@ void BlurEffect::reconfigure(ReconfigureFlags flags) void BlurEffect::slotWindowAdded(EffectWindow *w) { s_allWindows.push_back(w); - m_windows[w] = std::make_unique(m_config.get(), w, net_wm_blur_region); + m_windows[w] = std::make_unique(w, m_config.get(), net_wm_blur_region); windowExpandedGeometryChangedConnections[w] = connect(w, &EffectWindow::windowExpandedGeometryChanged, this, [this,w]() { if (w && w->isDesktop() && !effects->waylandDisplay()) { diff --git a/src/blurwindow.cpp b/src/blurwindow.cpp index 520a8d98ca..b5f841694e 100644 --- a/src/blurwindow.cpp +++ b/src/blurwindow.cpp @@ -19,9 +19,9 @@ namespace BetterBlur { -Window::Window(const WindowRuleList *windowRules, KWin::EffectWindow *w, long net_wm_blur_region) - : m_windowRules(windowRules), - w(w), +Window::Window(KWin::EffectWindow *w, const WindowRuleList *windowRules, long net_wm_blur_region) + : w(w), + m_windowRules(windowRules), net_wm_blur_region(net_wm_blur_region) { connect(w, &KWin::EffectWindow::windowDecorationChanged, this, [this]() { diff --git a/src/blurwindow.h b/src/blurwindow.h index 90bb46788e..93a4e8f26e 100644 --- a/src/blurwindow.h +++ b/src/blurwindow.h @@ -28,7 +28,7 @@ class Window : public QObject { Q_OBJECT public: - Window(const WindowRuleList *windowRules, KWin::EffectWindow *w, long net_wm_blur_region); + Window(KWin::EffectWindow *w, const WindowRuleList *windowRules, long net_wm_blur_region); ~Window() override; bool isMaximized() const; @@ -73,7 +73,6 @@ public Q_SLOTS: std::optional m_contentBlurRegion; std::optional m_frameBlurRegion; - long net_wm_blur_region = 0; bool m_blurWhenTransformed = false; #ifdef KWIN_6_2_OR_GREATER @@ -83,9 +82,10 @@ public Q_SLOTS: QSizeF m_size; bool m_hasWindowBehind = false; std::unique_ptr m_properties; - KWin::EffectWindow *w; + KWin::EffectWindow *w; const WindowRuleList *m_windowRules; + long net_wm_blur_region = 0; }; } \ No newline at end of file From 980b95ef2d89b2ca808456b2af1a42c25a3f13f9 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:35:27 +0100 Subject: [PATCH 08/19] blurwindow: use pointer to net_wm_blur_region --- src/blur.cpp | 2 +- src/blurwindow.cpp | 6 +++--- src/blurwindow.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/blur.cpp b/src/blur.cpp index a4d66068d0..632a67c19f 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -252,7 +252,7 @@ void BlurEffect::reconfigure(ReconfigureFlags flags) void BlurEffect::slotWindowAdded(EffectWindow *w) { s_allWindows.push_back(w); - m_windows[w] = std::make_unique(w, m_config.get(), net_wm_blur_region); + m_windows[w] = std::make_unique(w, m_config.get(), &net_wm_blur_region); windowExpandedGeometryChangedConnections[w] = connect(w, &EffectWindow::windowExpandedGeometryChanged, this, [this,w]() { if (w && w->isDesktop() && !effects->waylandDisplay()) { diff --git a/src/blurwindow.cpp b/src/blurwindow.cpp index b5f841694e..194149d786 100644 --- a/src/blurwindow.cpp +++ b/src/blurwindow.cpp @@ -19,7 +19,7 @@ namespace BetterBlur { -Window::Window(KWin::EffectWindow *w, const WindowRuleList *windowRules, long net_wm_blur_region) +Window::Window(KWin::EffectWindow *w, const WindowRuleList *windowRules, long *net_wm_blur_region) : w(w), m_windowRules(windowRules), net_wm_blur_region(net_wm_blur_region) @@ -61,8 +61,8 @@ void Window::updateBlurRegion(bool geometryChanged) std::optional content; std::optional frame; - if (net_wm_blur_region != XCB_ATOM_NONE) { - const QByteArray value = w->readProperty(net_wm_blur_region, XCB_ATOM_CARDINAL, 32); + if (*net_wm_blur_region != XCB_ATOM_NONE) { + const QByteArray value = w->readProperty(*net_wm_blur_region, XCB_ATOM_CARDINAL, 32); QRegion region; if (value.size() > 0 && !(value.size() % (4 * sizeof(uint32_t)))) { const uint32_t *cardinals = reinterpret_cast(value.constData()); diff --git a/src/blurwindow.h b/src/blurwindow.h index 93a4e8f26e..9e73bce42e 100644 --- a/src/blurwindow.h +++ b/src/blurwindow.h @@ -28,7 +28,7 @@ class Window : public QObject { Q_OBJECT public: - Window(KWin::EffectWindow *w, const WindowRuleList *windowRules, long net_wm_blur_region); + Window(KWin::EffectWindow *w, const WindowRuleList *windowRules, long *net_wm_blur_region); ~Window() override; bool isMaximized() const; @@ -85,7 +85,7 @@ public Q_SLOTS: KWin::EffectWindow *w; const WindowRuleList *m_windowRules; - long net_wm_blur_region = 0; + long *net_wm_blur_region; }; } \ No newline at end of file From 348b15cde208a2529bd98328ad6d21898a604d97 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:02:29 +0100 Subject: [PATCH 09/19] blur/static-blur: use window's blur strength instead of the global one --- docs/configuration.md | 2 +- src/blur.cpp | 42 ++++++++++++++++++++++-------------------- src/blur.h | 13 ++++++------- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 0335a2a9bb..7913a06b9a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -82,7 +82,7 @@ Properties don't have default values. Unset properties don't override properties |:-----------------------------|------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **BlurContent** | ``bool`` | Whether to force blur the window's content, excluding decorations. | | **BlurDecorations** | ``bool`` | See *Blur window decorations as well* in the *Simple* section. The difference is that in window rules it's possible to blur only the decorations and the corner radius is specified in the *CornerRadius* property. | -| **BlurStrength** | ``int (1 - 15)`` | Currently doesn't affect static blur, which still uses the global blur strength. | +| **BlurStrength** | ``int (1 - 15)`` | | | **CornerRadius** | ``float (>= 0)`` | Corner radius for the blur region.

A single number specifies the same radius for both top and bottom corners. Example: ``12.5``
Two numbers separated by a space specify different radii for top and bottom corners respectively. Example: ``0 12.5``

Top corner radius will only be applied to windows with force-blurred decorations or no decorations. | | **CornerAntialiasing** | ``float (>= 0)`` | Makes rounded corners appear smoother. Recommended value is 1. | | **ForceTransparency** | ``bool`` | See *Treat windows as transparent* in the *Simple* section. | diff --git a/src/blur.cpp b/src/blur.cpp index 632a67c19f..21706c1c30 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -256,7 +256,7 @@ void BlurEffect::slotWindowAdded(EffectWindow *w) windowExpandedGeometryChangedConnections[w] = connect(w, &EffectWindow::windowExpandedGeometryChanged, this, [this,w]() { if (w && w->isDesktop() && !effects->waylandDisplay()) { - m_staticBlurTextures.erase(nullptr); + m_staticBlurTextures.clear(); } }); @@ -370,7 +370,10 @@ void BlurEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std:: const QRegion blurArea = blurWindow->blurRegion().translated(w->pos().toPoint()); - bool staticBlur = blurWindow->properties()->staticBlur() && m_staticBlurTextures.contains(m_currentScreen) && !blurArea.isEmpty(); + bool staticBlur = blurWindow->properties()->staticBlur() + && !blurArea.isEmpty() + && m_staticBlurTextures.contains(m_currentScreen) + && m_staticBlurTextures[m_currentScreen].contains(blurWindow->properties()->blurStrength()); if (staticBlur) { if (!blurWindow->properties()->windowOpacityAffectsBlur()) { data.opaque += blurArea; @@ -444,17 +447,17 @@ void BlurEffect::drawWindow(const RenderTarget &renderTarget, const RenderViewpo { auto blurWindow = m_windows[w].get(); if (blurWindow->shouldBlur(mask, data)) { - blur(blurWindow, blurWindow->render[m_currentScreen], renderTarget, viewport, mask, region, data); + blur(blurWindow, blurWindow->properties()->blurStrength() - 1, blurWindow->render[m_currentScreen], renderTarget, viewport, mask, region, data); } // Draw the window over the blurred area effects->drawWindow(renderTarget, viewport, w, mask, region, data); } -GLTexture *BlurEffect::ensureStaticBlurTexture(const Output *output, const RenderTarget &renderTarget) +GLTexture *BlurEffect::ensureStaticBlurTexture(const Output *output, const int &strength, const RenderTarget &renderTarget) { - if (m_staticBlurTextures.contains(output)) { - return m_staticBlurTextures[output].get(); + if (m_staticBlurTextures.contains(output) && m_staticBlurTextures[output].contains(strength)) { + return m_staticBlurTextures[output][strength].get(); } if (effects->waylandDisplay() && !output) { @@ -466,13 +469,13 @@ GLTexture *BlurEffect::ensureStaticBlurTexture(const Output *output, const Rende textureFormat = renderTarget.texture()->internalFormat(); } GLTexture *texture = effects->waylandDisplay() - ? createStaticBlurTextureWayland(output, renderTarget, textureFormat) - : createStaticBlurTextureX11(textureFormat); + ? createStaticBlurTextureWayland(output, strength, renderTarget, textureFormat) + : createStaticBlurTextureX11(strength, textureFormat); if (!texture) { return nullptr; } - return (m_staticBlurTextures[output] = std::unique_ptr(texture)).get(); + return (m_staticBlurTextures[output][strength] = std::unique_ptr(texture)).get(); } GLTexture *BlurEffect::ensureNoiseTexture() @@ -511,7 +514,7 @@ GLTexture *BlurEffect::ensureNoiseTexture() return noiseTexture.get(); } -void BlurEffect::blur(BetterBlur::Window *w, BetterBlur::BlurRenderData &renderInfo, const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, WindowPaintData &data) +void BlurEffect::blur(BetterBlur::Window *w, const int &strength, BetterBlur::BlurRenderData &renderInfo, const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, WindowPaintData &data) { Q_UNUSED(mask) @@ -580,12 +583,11 @@ void BlurEffect::blur(BetterBlur::Window *w, BetterBlur::BlurRenderData &renderI // reset. GLTexture *staticBlurTexture = nullptr; if (w && w->properties()->staticBlur()) { - staticBlurTexture = ensureStaticBlurTexture(m_currentScreen, renderTarget); + staticBlurTexture = ensureStaticBlurTexture(m_currentScreen, strength, renderTarget); } - auto blurStrength = (w ? w->properties()->blurStrength() : BetterBlur::Config::blurStrength()) - 1; - size_t iterationCount = blurStrengthValues[blurStrength].iteration; - auto offset = blurStrengthValues[blurStrength].offset; + size_t iterationCount = blurStrengthValues[strength].iteration; + auto offset = blurStrengthValues[strength].offset; if (!staticBlurTexture && (renderInfo.framebuffers.size() != (iterationCount + 1) @@ -854,7 +856,7 @@ void BlurEffect::blur(BetterBlur::Window *w, BetterBlur::BlurRenderData &renderI vbo->unbindArrays(); } -void BlurEffect::blur(GLTexture *texture) +void BlurEffect::blur(GLTexture *texture, const int &strength) { const QRect textureRect = QRect(0, 0, texture->width(), texture->height()); auto blurredFramebuffer = std::make_unique(texture); @@ -865,7 +867,7 @@ void BlurEffect::blur(GLTexture *texture) WindowPaintData data; GLFramebuffer::pushFramebuffer(blurredFramebuffer.get()); - blur(nullptr, renderInfo, renderTarget, renderViewport, 0, textureRect, data); + blur(nullptr, strength, renderInfo, renderTarget, renderViewport, 0, textureRect, data); GLFramebuffer::popFramebuffer(); } @@ -901,7 +903,7 @@ GLTexture *BlurEffect::wallpaper(EffectWindow *desktop, const qreal &scale, cons return texture.release(); } -GLTexture *BlurEffect::createStaticBlurTextureWayland(const Output *output, const RenderTarget &renderTarget, const GLenum &textureFormat) +GLTexture *BlurEffect::createStaticBlurTextureWayland(const Output *output, const int &strength, const RenderTarget &renderTarget, const GLenum &textureFormat) { EffectWindow *desktop = nullptr; for (EffectWindow *w : effects->stackingOrder()) { @@ -954,13 +956,13 @@ GLTexture *BlurEffect::createStaticBlurTextureWayland(const Output *output, cons ShaderManager::instance()->popShader(); if (BetterBlur::Config::fakeBlurCustomImageBlur()) { - blur(texture.get()); + blur(texture.get(), strength); } return texture.release(); } -GLTexture *BlurEffect::createStaticBlurTextureX11(const GLenum &textureFormat) +GLTexture *BlurEffect::createStaticBlurTextureX11(const int &strength, const GLenum &textureFormat) { std::vector desktops; QRegion desktopGeometries; @@ -997,7 +999,7 @@ GLTexture *BlurEffect::createStaticBlurTextureX11(const GLenum &textureFormat) } if (BetterBlur::Config::fakeBlurCustomImageBlur()) { - blur(texture.get()); + blur(texture.get(), strength); } QMatrix4x4 projectionMatrix; diff --git a/src/blur.h b/src/blur.h index 1d618b5079..36ddd9699d 100644 --- a/src/blur.h +++ b/src/blur.h @@ -17,7 +17,6 @@ #include - namespace KWin { @@ -66,15 +65,15 @@ public Q_SLOTS: /* * @param w The pointer to the window being blurred, nullptr if an image is being blurred. */ - void blur(BetterBlur::Window *w, BetterBlur::BlurRenderData &renderInfo, const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, WindowPaintData &data); - void blur(GLTexture *texture); + void blur(BetterBlur::Window *w, const int &strength, BetterBlur::BlurRenderData &renderInfo, const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, WindowPaintData &data); + void blur(GLTexture *texture, const int &strength); /** * @param output Can be nullptr. * @remark This method shall not be called outside of BlurEffect::blur. * @return The cached static blur texture. The texture will be created if it doesn't exist. */ - GLTexture *ensureStaticBlurTexture(const Output *output, const RenderTarget &renderTarget); + GLTexture *ensureStaticBlurTexture(const Output *output, const int &strength, const RenderTarget &renderTarget); GLTexture *ensureNoiseTexture(); /** @@ -89,13 +88,13 @@ public Q_SLOTS: * @remark This method shall not be called outside of BlurEffect::blur. * @return A pointer to the texture, or nullptr if an error occurred. */ - GLTexture *createStaticBlurTextureWayland(const Output *output, const RenderTarget &renderTarget, const GLenum &textureFormat); + GLTexture *createStaticBlurTextureWayland(const Output *output, const int &strength, const RenderTarget &renderTarget, const GLenum &textureFormat); /** * Creates a composite static blur texture containing images for all screens. * @return A pointer to the texture, or nullptr if an error occurred. */ - GLTexture *createStaticBlurTextureX11(const GLenum &textureFormat); + GLTexture *createStaticBlurTextureX11(const int &strength, const GLenum &textureFormat); private: struct @@ -172,7 +171,7 @@ public Q_SLOTS: QList blurStrengthValues; - std::unordered_map> m_staticBlurTextures; + std::unordered_map>> m_staticBlurTextures; QImage m_staticBlurImage; QMap windowExpandedGeometryChangedConnections; From 70a827d5fe9f95fbbe3428dbfaa61a2b721d36da Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:46:59 +0100 Subject: [PATCH 10/19] docs/configuration: fix regular expressions It's necessary to use two backslashes when escaping, not one. --- docs/configuration.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 7913a06b9a..5dc13dd7d1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -64,14 +64,14 @@ Rules are not evaluated in order as they appear in the file. At least 1 (or 0 if none specified) condition must be satisfied in order for this rule to be applied. All specified subconditions in a condition must be satisfied. -| Property | Type | Description | -|-----------------------------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **Negate** | ``enum (WindowClass, WindowState, WindowType)``
Multiple values are allowed. | Which subconditions should be negated.

Separated by space. | -| **HasWindowBehind** | ``bool`` | Whether only windows that (don't) have another window (excluding desktops) behind them should satisfy this condition. | -| **Internal** | ``bool`` | Internal KWin windows. | -| **WindowClass** | ``string`` or ``regex`` | A regular expression executed on the window class. Only a partial match is required.

For an exact match, use ``^class$``.
To specify multiple classes, separate them by ``\|``: ``class1\|class2``.
A dot matches any character, which can be prevented by escaping it: ``\.``. | -| **WindowState** | ``enum (Fullscreen, Maximized)``
Multiple values are allowed. | Separated by space. | -| **WindowType** | ``enum (Dialog, Dock, Normal, Menu, Toolbar, Tooltip, Utility)``
Multiple values are allowed. | Separated by space. | +| Property | Type | Description | +|-----------------------------|--------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Negate** | ``enum (WindowClass, WindowState, WindowType)``
Multiple values are allowed. | Which subconditions should be negated.

Separated by space. | +| **HasWindowBehind** | ``bool`` | Whether only windows that (don't) have another window (excluding desktops) behind them should satisfy this condition. | +| **Internal** | ``bool`` | Internal KWin windows. | +| **WindowClass** | ``string`` or ``regex`` | A regular expression executed on the window class. Only a partial match is required.

For an exact match, use ``^class$``.
To specify multiple classes, separate them by ``\|``: ``class1\|class2``.
A dot matches any character, which can be prevented by escaping it: ``\\.``. | +| **WindowState** | ``enum (Fullscreen, Maximized)``
Multiple values are allowed. | Separated by space. | +| **WindowType** | ``enum (Dialog, Dock, Normal, Menu, Toolbar, Tooltip, Utility)``
Multiple values are allowed. | Separated by space. | ### Properties **Group**: [WindowRules][\$RuleName][Properties] @@ -97,7 +97,7 @@ WindowOpacityAffectsBlur = false [WindowRules][ForceBlur][Conditions][0] -WindowClass = clementine|kate|kwrite|org\.freedesktop\.impl\.portal\.desktop\.kde|plasmashell +WindowClass = clementine|kate|kwrite|org\\.freedesktop\\.impl\\.portal\\.desktop\\.kde|plasmashell WindowType = Dialog Normal Menu Toolbar Tooltip Utility [WindowRules][ForceBlur][Conditions][1] From cfbddf090460519c08e43c339f99e93337b3cf91 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:50:09 +0100 Subject: [PATCH 11/19] docs/configuration/window-rules: add examples for blur strength --- docs/configuration.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 5dc13dd7d1..cb0875867c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -91,7 +91,8 @@ Properties don't have default values. Unset properties don't override properties ## Example ``` -[WindowRules][AllWindows][Properties] +[WindowRules][Global][Properties] +BlurStrength = 9 CornerAntialiasing = true WindowOpacityAffectsBlur = false @@ -116,6 +117,20 @@ HasWindowBehind = false StaticBlur = true +# Reduce blur strength for windows on top of other windows and panels +[WindowRules][LowBlurStrength] +Priority = 1 + +[WindowRules][LowBlurStrength][Conditions][0] +HasWindowBehind = true + +[WindowRules][LowBlurStrength][Conditions][1] +WindowType = Dock + +[WindowRules][LowBlurStrength][Properties] +BlurStrength = 3 + + [WindowRules][WindowCorners][Conditions][0] Negate = WindowState WindowState = Fullscreen Maximized From 655cae67dd06ae9089783f128c3328ecdf22a031 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:32:14 +0100 Subject: [PATCH 12/19] docs/configuration/window-rules: fix corner antialiasing in example --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index cb0875867c..bbf8b9c844 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -93,7 +93,7 @@ Properties don't have default values. Unset properties don't override properties ``` [WindowRules][Global][Properties] BlurStrength = 9 -CornerAntialiasing = true +CornerAntialiasing = 1 WindowOpacityAffectsBlur = false From c2725e1d5930b2c260e2f1c52e4173b72890d5a8 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Thu, 28 Nov 2024 08:52:08 +0100 Subject: [PATCH 13/19] windowrules/simple: blur dialogs --- src/windowrules/windowrulelist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windowrules/windowrulelist.cpp b/src/windowrules/windowrulelist.cpp index d148f977b4..8902ee7c89 100644 --- a/src/windowrules/windowrulelist.cpp +++ b/src/windowrules/windowrulelist.cpp @@ -168,7 +168,7 @@ void WindowRuleList::loadSimple() if (!regexes.empty()) { forceBlurRuleCondition->setWindowClass(QRegularExpression(regexes.join("|"))); } - WindowTypes types = WindowType::Normal; + WindowTypes types = static_cast(WindowType::Normal) | WindowType::Dialog; if (betterBlurV1Group.readEntry("BlurMenus", false)) { types |= WindowType::Menu; } From 467be21e0366a2b6e1a78312844ff642d4505d42 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:46:26 +0100 Subject: [PATCH 14/19] remove uused BlurEffect::allWindows() --- src/blur.cpp | 10 ---------- src/blur.h | 11 ----------- 2 files changed, 21 deletions(-) diff --git a/src/blur.cpp b/src/blur.cpp index 21706c1c30..375a7755da 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -61,7 +61,6 @@ static const QByteArray s_blurAtomName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEH BlurManagerInterface *BlurEffect::s_blurManager = nullptr; QTimer *BlurEffect::s_blurManagerRemoveTimer = nullptr; -std::vector BlurEffect::s_allWindows; BlurEffect::BlurEffect() { @@ -251,7 +250,6 @@ void BlurEffect::reconfigure(ReconfigureFlags flags) void BlurEffect::slotWindowAdded(EffectWindow *w) { - s_allWindows.push_back(w); m_windows[w] = std::make_unique(w, m_config.get(), &net_wm_blur_region); windowExpandedGeometryChangedConnections[w] = connect(w, &EffectWindow::windowExpandedGeometryChanged, this, [this,w]() { @@ -275,9 +273,6 @@ void BlurEffect::slotWindowDeleted(EffectWindow *w) disconnect(*it); windowExpandedGeometryChangedConnections.erase(it); } - if (auto it = std::find(s_allWindows.begin(), s_allWindows.end(), w); it != s_allWindows.end()) { - s_allWindows.erase(it); - } m_windows.erase(w); } @@ -1015,11 +1010,6 @@ GLTexture *BlurEffect::createStaticBlurTextureX11(const int &strength, const GLe return compositeTexture.release(); } -const std::vector BlurEffect::allWindows() -{ - return BlurEffect::s_allWindows; -} - bool BlurEffect::isActive() const { return m_valid && !effects->isScreenLocked(); diff --git a/src/blur.h b/src/blur.h index 36ddd9699d..3036418b4c 100644 --- a/src/blur.h +++ b/src/blur.h @@ -33,8 +33,6 @@ class BlurEffect : public KWin::Effect static bool supported(); static bool enabledByDefault(); - static const std::vector allWindows(); - void reconfigure(ReconfigureFlags flags) override; void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override; void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) override; @@ -180,15 +178,6 @@ public Q_SLOTS: std::unique_ptr m_config; - /** - * Stores all currently open windows, even those that aren't blurred. Used for determining whether windows are - * overlapping. - * - * Objects retrieved from effects->stackingOrder() and workspace()->stackingOrder() appear to be deleted when - * BlurEffect::prePaintWindow is running, so that can't be used. - */ - static std::vector s_allWindows; - static BlurManagerInterface *s_blurManager; static QTimer *s_blurManagerRemoveTimer; }; From 4716c35f386b223632eee7a5868ac77bac0e3ffe Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:49:11 +0100 Subject: [PATCH 15/19] rename BlurEffect::m_config to BlurEffect::m_windowRules --- src/blur.cpp | 6 +++--- src/blur.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/blur.cpp b/src/blur.cpp index 375a7755da..ad9f7aedf6 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -64,7 +64,7 @@ QTimer *BlurEffect::s_blurManagerRemoveTimer = nullptr; BlurEffect::BlurEffect() { - m_config = std::make_unique(); + m_windowRules = std::make_unique(); BetterBlur::Config::instance(effects->config()); ensureResources(); @@ -233,7 +233,7 @@ void BlurEffect::reconfigure(ReconfigureFlags flags) Q_UNUSED(flags) BetterBlur::Config::self()->read(); - m_config->load(); + m_windowRules->load(); m_noiseStrength = BetterBlur::Config::noiseStrength(); m_staticBlurImage = QImage(BetterBlur::Config::fakeBlurImage()); @@ -250,7 +250,7 @@ void BlurEffect::reconfigure(ReconfigureFlags flags) void BlurEffect::slotWindowAdded(EffectWindow *w) { - m_windows[w] = std::make_unique(w, m_config.get(), &net_wm_blur_region); + m_windows[w] = std::make_unique(w, m_windowRules.get(), &net_wm_blur_region); windowExpandedGeometryChangedConnections[w] = connect(w, &EffectWindow::windowExpandedGeometryChanged, this, [this,w]() { if (w && w->isDesktop() && !effects->waylandDisplay()) { diff --git a/src/blur.h b/src/blur.h index 3036418b4c..bad79354f2 100644 --- a/src/blur.h +++ b/src/blur.h @@ -176,7 +176,7 @@ public Q_SLOTS: QMap screenChangedConnections; std::unordered_map> m_windows; - std::unique_ptr m_config; + std::unique_ptr m_windowRules; static BlurManagerInterface *s_blurManager; static QTimer *s_blurManagerRemoveTimer; From 3c84e7dbf762af02752eaa9dc08bfb083a920687 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Sat, 7 Dec 2024 15:54:16 +0100 Subject: [PATCH 16/19] blur/static-blur: only invalidate texture on desktop update when the source is desktop wallpaper --- src/blur.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/blur.cpp b/src/blur.cpp index ad9f7aedf6..03ea0871bd 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -385,7 +385,10 @@ void BlurEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std:: } } - if (!m_staticBlurTextures.empty() && w->isDesktop() && w->frameGeometry() == data.paint.boundingRect()) { + if (BetterBlur::Config::fakeBlurImageSourceDesktopWallpaper() + && !m_staticBlurTextures.empty() + && w->isDesktop() + && w->frameGeometry() == data.paint.boundingRect()) { m_staticBlurTextures.erase(m_currentScreen); } From 0a751c02543a0a910b42670ae755dcd0d8fedf20 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:22:13 +0100 Subject: [PATCH 17/19] fix some stuff broken by the merge --- docs/configuration.md | 18 +----------------- src/blur.cpp | 12 ------------ src/blurwindow.cpp | 12 +++++++----- src/blurwindow.h | 4 ++-- 4 files changed, 10 insertions(+), 36 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 8856a86d0a..bbf8b9c844 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -23,16 +23,10 @@ Obvious options are not explained here. | **Window top corner radius** | Requires *Blur window decorations as well* to be enabled for windows with decorations. | | **Anti-aliasing** | Makes rounded corners appear smoother. | -<<<<<<< HEAD ## Static blur When enabled, a cached texture will be painted behind the window instead of actually blurring the background (dynamic blur). The blurred areas of the window will also be marked as opaque, causing KWin to not paint anything behind the window. Both things result in lower resource usage. -======= -# Static blur -When enabled, the blur texture will be cached and reused. The blurred areas of the window will be marked as opaque, resulting in KWin not painting anything behind them. -Only one image per screen is cached at a time. ->>>>>>> origin/main Static blur is mainly intended for laptop users who want longer battery life while still having blur everywhere. @@ -48,17 +42,11 @@ Static blur is mainly intended for laptop users who want longer battery life whi |-------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Convert simple config to window rules** | Whether to automatically convert simple configuration into window rules. Disabling this will cause most options in the simple configuration UI to not have an effect anymore. | -<<<<<<< HEAD # Window rules Configuration UI for window rules will be added in v2. The configuration file is located at ``~/.config/kwinbetterblurrc`` and isn't created automatically. -======= -### Image source -The image to use for static blur. ->>>>>>> origin/main Simple configuration is converted into multiple rules with priorities lower than 0. They are not added into the configuration file. This behavior can be disabled in the configuration UI in the *Window Rules* tab. -<<<<<<< HEAD An example is provided at the end. ## File structure @@ -157,8 +145,4 @@ WindowType = Menu [WindowRules][MenuCorners][Properties] CornerRadius = 10 -``` -======= -### Blur image -Whether to blur the image used for static blur. This is only done once. ->>>>>>> origin/main +``` \ No newline at end of file diff --git a/src/blur.cpp b/src/blur.cpp index ca80e41773..1eed7166a4 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -24,12 +24,6 @@ #include "wayland/surface.h" #include "kwin/window.h" -#ifdef KWIN_6_2_OR_GREATER -#include "scene/decorationitem.h" -#include "scene/surfaceitem.h" -#include "scene/windowitem.h" -#endif - #include #include #include @@ -43,12 +37,6 @@ #include #include -#ifdef KDECORATION3 -#include -#else -#include -#endif - #include Q_LOGGING_CATEGORY(KWIN_BLUR, "kwin_better_blur", QtWarningMsg) diff --git a/src/blurwindow.cpp b/src/blurwindow.cpp index 12ff19a51d..da49dd1c1d 100644 --- a/src/blurwindow.cpp +++ b/src/blurwindow.cpp @@ -12,9 +12,11 @@ #include "scene/windowitem.h" #endif +#ifdef KDECORATION3 +#include +#else #include - -#include +#endif namespace BetterBlur { @@ -27,7 +29,7 @@ Window::Window(KWin::EffectWindow *w, const WindowRuleList *windowRules, long *n connect(w, &KWin::EffectWindow::windowDecorationChanged, this, [this]() { setupDecorationConnections(); }); - windowExpandedGeometryChangedConnection = connect(w, &KWin::EffectWindow::windowExpandedGeometryChanged, this, &Window::slotWindowExpandedGeometryChanged); + windowFrameGeometryChangedConnection = connect(w, &KWin::EffectWindow::windowFrameGeometryChanged, this, &Window::slotWindowFrameGeometryChanged); setupDecorationConnections(); if (auto surf = w->surface()) { @@ -43,7 +45,7 @@ Window::Window(KWin::EffectWindow *w, const WindowRuleList *windowRules, long *n Window::~Window() { disconnect(windowBlurChangedConnection); - disconnect(windowExpandedGeometryChangedConnection); + disconnect(windowFrameGeometryChangedConnection); } bool Window::isMaximized() const @@ -200,7 +202,7 @@ bool Window::decorationSupportsBlurBehind() const return w->decoration() && !w->decoration()->blurRegion().isNull(); } -void Window::slotWindowExpandedGeometryChanged(KWin::EffectWindow *w) +void Window::slotWindowFrameGeometryChanged(KWin::EffectWindow *w) { if (w->size() != m_size) { updateProperties(); diff --git a/src/blurwindow.h b/src/blurwindow.h index 9e73bce42e..38ff9be3ea 100644 --- a/src/blurwindow.h +++ b/src/blurwindow.h @@ -59,7 +59,7 @@ class Window : public QObject std::unordered_map render; public Q_SLOTS: - void slotWindowExpandedGeometryChanged(KWin::EffectWindow *w); + void slotWindowFrameGeometryChanged(KWin::EffectWindow *w); // void slotDecorationBlurRegionChanged(); private: @@ -69,7 +69,7 @@ public Q_SLOTS: bool decorationSupportsBlurBehind() const; QMetaObject::Connection windowBlurChangedConnection; - QMetaObject::Connection windowExpandedGeometryChangedConnection; + QMetaObject::Connection windowFrameGeometryChangedConnection; std::optional m_contentBlurRegion; std::optional m_frameBlurRegion; From 48d8c9a8323bb4aabf9e9ebedc1849d06e297317 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Sun, 5 Jan 2025 15:30:13 +0100 Subject: [PATCH 18/19] flake/inputs/nixpkgs: use nixos-unstable --- flake.lock | 8 ++++---- flake.nix | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/flake.lock b/flake.lock index 179170fd4e..0be498cd7b 100644 --- a/flake.lock +++ b/flake.lock @@ -2,17 +2,17 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1735471104, - "narHash": "sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs=", + "lastModified": 1735834308, + "narHash": "sha256-dklw3AXr3OGO4/XT1Tu3Xz9n/we8GctZZ75ZWVqAVhk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "88195a94f390381c6afcdaa933c2f6ff93959cb4", + "rev": "6df24922a1400241dae323af55f30e4318a6ca65", "type": "github" }, "original": { "owner": "NixOS", + "ref": "nixos-unstable", "repo": "nixpkgs", - "rev": "88195a94f390381c6afcdaa933c2f6ff93959cb4", "type": "github" } }, diff --git a/flake.nix b/flake.nix index 4994bd92bf..d8ae15bd7a 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "Fork of the KWin Blur effect for KDE Plasma 6 with additional features (including force blur) and bug fixes"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/88195a94f390381c6afcdaa933c2f6ff93959cb4"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; utils.url = "github:numtide/flake-utils"; }; From 94af923eb2782c1b8ba2a7d5fc2a49a8bdfc49e9 Mon Sep 17 00:00:00 2001 From: taj-ny <79316397+taj-ny@users.noreply.github.com> Date: Sun, 5 Jan 2025 15:40:01 +0100 Subject: [PATCH 19/19] window: don't update properties on geometry change --- src/blurwindow.cpp | 7 +------ src/blurwindow.h | 3 +-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/blurwindow.cpp b/src/blurwindow.cpp index da49dd1c1d..500049ee04 100644 --- a/src/blurwindow.cpp +++ b/src/blurwindow.cpp @@ -202,14 +202,9 @@ bool Window::decorationSupportsBlurBehind() const return w->decoration() && !w->decoration()->blurRegion().isNull(); } -void Window::slotWindowFrameGeometryChanged(KWin::EffectWindow *w) +void Window::slotWindowFrameGeometryChanged() { - if (w->size() != m_size) { - updateProperties(); - } - updateBlurRegion(true); - m_size = w->size(); } void Window::setupDecorationConnections() diff --git a/src/blurwindow.h b/src/blurwindow.h index 38ff9be3ea..0403fcc475 100644 --- a/src/blurwindow.h +++ b/src/blurwindow.h @@ -59,7 +59,7 @@ class Window : public QObject std::unordered_map render; public Q_SLOTS: - void slotWindowFrameGeometryChanged(KWin::EffectWindow *w); + void slotWindowFrameGeometryChanged(); // void slotDecorationBlurRegionChanged(); private: @@ -79,7 +79,6 @@ public Q_SLOTS: std::unique_ptr m_windowEffect; #endif - QSizeF m_size; bool m_hasWindowBehind = false; std::unique_ptr m_properties;