diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 8fc5cd3..5a9af3c 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -custom: https://www.paypal.com/donate/?business=WYGJ6T4J23P2L&no_recurring=0¤cy_code=CAD +github: USERNAME diff --git a/.github/workflows/arch.yml b/.github/workflows/arch.yml index 2137aea..0cc4ab2 100644 --- a/.github/workflows/arch.yml +++ b/.github/workflows/arch.yml @@ -27,10 +27,10 @@ jobs: steps: - name: Refresh Packages - run: yes | pacman -Syu + run: pacman -Syu --noconfirm - name: Install Dependencies - run: yes | pacman -S git cmake extra-cmake-modules qt5-tools kwin wireplumber + run: pacman -S --noconfirm git cmake extra-cmake-modules qt5-tools kwin wireplumber - name: Check out repository uses: actions/checkout@v3 diff --git a/.github/workflows/debian12.yml b/.github/workflows/debian12.yml index 73a2fd3..be68082 100644 --- a/.github/workflows/debian12.yml +++ b/.github/workflows/debian12.yml @@ -34,7 +34,7 @@ jobs: continue-on-error: true - name: Install Dependencies - run: apt -y install cmake g++ gettext extra-cmake-modules qttools5-dev libqt5x11extras5-dev libkf5configwidgets-dev libkf5globalaccel-dev libkf5notifications-dev kwin-dev + run: apt -y install cmake g++ gettext extra-cmake-modules qttools5-dev libkf5configwidgets-dev kwin-dev - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. diff --git a/.github/workflows/glslangValidator.yml b/.github/workflows/glslangValidator.yml index 744974c..243992d 100644 --- a/.github/workflows/glslangValidator.yml +++ b/.github/workflows/glslangValidator.yml @@ -28,4 +28,4 @@ jobs: run: sudo apt update && sudo apt install -y glslang-tools - name: Validate Shaders - run: glslangValidator src/shaders_110/shapecorners.frag src/shaders_140/shapecorners.frag + run: glslangValidator src/shaders/shapecorners.frag src/shaders/shapecorners_core.frag diff --git a/.github/workflows/kubuntu2204-backports.yml b/.github/workflows/kubuntu2204-backports.yml index c3acfae..4813c0c 100644 --- a/.github/workflows/kubuntu2204-backports.yml +++ b/.github/workflows/kubuntu2204-backports.yml @@ -39,7 +39,7 @@ jobs: run: sudo add-apt-repository -y ppa:kubuntu-ppa/backports-extra - name: Install Dependencies - run: sudo apt install -y gettext extra-cmake-modules qttools5-dev libqt5x11extras5-dev libkf5configwidgets-dev libkf5globalaccel-dev libkf5notifications-dev kwin-dev + run: sudo apt install -y gettext extra-cmake-modules qttools5-dev libkf5configwidgets-dev kwin-dev - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. diff --git a/.github/workflows/neon-unstable.yml b/.github/workflows/neon-unstable.yml index 0cce047..b5cbcc7 100644 --- a/.github/workflows/neon-unstable.yml +++ b/.github/workflows/neon-unstable.yml @@ -1,7 +1,14 @@ name: KDE Neon (Unstable) on: - workflow_dispatch: + push: + branches: [ master ] + paths-ignore: [ '**.md', '**.png' ] + pull_request: + branches: [ master ] + paths-ignore: [ '**.md', '**.png' ] + schedule: + - cron: "0 0 * * *" env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) @@ -23,11 +30,19 @@ jobs: uses: actions/checkout@v3 - name: Refresh KDE Neon Packages - run: apt update && apt -y dist-upgrade + run: apt update && apt -y --allow-downgrades dist-upgrade + continue-on-error: true + + - name: Second refresh KDE Neon Packages + run: apt update && apt -y --allow-downgrades dist-upgrade + continue-on-error: true + + - name: Third refresh KDE Neon Packages + run: apt update && apt -y --allow-downgrades autoremove continue-on-error: true - name: Install Dependencies - run: apt -y install cmake g++ gettext extra-cmake-modules qttools5-dev libqt5x11extras5-dev libkf5configwidgets-dev libkf5globalaccel-dev libkf5notifications-dev kwin-dev + run: apt -y install cmake g++ gettext extra-cmake-modules qt6-base-dev-tools kf6-kcmutils-dev kwin-dev - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. diff --git a/.github/workflows/neon.yml b/.github/workflows/neon.yml index f84cecd..da5347e 100644 --- a/.github/workflows/neon.yml +++ b/.github/workflows/neon.yml @@ -34,7 +34,7 @@ jobs: continue-on-error: true - name: Install Dependencies - run: apt -y install cmake g++ gettext extra-cmake-modules qttools5-dev libqt5x11extras5-dev libkf5configwidgets-dev libkf5globalaccel-dev libkf5notifications-dev kwin-dev + run: apt -y install cmake g++ gettext extra-cmake-modules qttools5-dev libkf5configwidgets-dev kwin-dev - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. diff --git a/.gitignore b/.gitignore index 864c833..733dbb2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ /cmake-build-*/ /qt5build/ /build/ +/Testing/ CMakeLists.txt.user diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c333af..fa5ead6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,11 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.20) -project(kwin-effects-yaml) +project(kwin4_effect_shapecorners) + +set(KF_MIN_VERSION "5.78") +set(QT_MIN_VERSION "5.15") +set(QT_MAJOR_VERSION 5) -set(KF5_MIN_VERSION "5.78") set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -12,24 +15,16 @@ if(NOT CMAKE_BUILD_TYPE) endif() set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DQT_NO_DEBUG_OUTPUT") -find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) +find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ) - -find_package(Qt5 CONFIG REQUIRED COMPONENTS - Gui - Core - DBus - UiTools - Widgets - X11Extras - OpenGL - Network - Xml -) +if(ECM_VERSION VERSION_GREATER "5.200.0") + set(QT_MIN_VERSION "6.4.0") + set(QT_MAJOR_VERSION 6) +endif() include(FeatureSummary) include(KDEInstallDirs) @@ -37,28 +32,46 @@ include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) # required frameworks by Core -find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS - Config +find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS ConfigWidgets - CoreAddons - GlobalAccel - WindowSystem ) +if(${QT_MAJOR_VERSION} EQUAL 6) + find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS + KCMUtils + ) + + find_package(KWin REQUIRED COMPONENTS + kwineffects + ) + set(KWIN_EFFECT_INCLUDE_FILE "/usr/include/kwin/effect/effect.h") +else() + find_package(KWinEffects REQUIRED COMPONENTS + kwineffects + kwinglutils + ) + set(KWIN_EFFECT_INCLUDE_FILE "/usr/include/kwineffects.h") +endif () +find_package(epoxy REQUIRED) find_package(XCB REQUIRED COMPONENTS XCB) -find_package(KWinEffects REQUIRED COMPONENTS - kwineffects - kwinglutils -) - find_package(KWinDBusInterface CONFIG REQUIRED) -find_package(epoxy REQUIRED) add_subdirectory(src) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) +execute_process( + COMMAND sh -c "grep '#define KWIN_EFFECT_API_VERSION_MINOR' ${KWIN_EFFECT_INCLUDE_FILE} | awk '{print \$NF}'" + OUTPUT_VARIABLE KWIN_EFFECT_API_VERSION_MINOR OUTPUT_STRIP_TRAILING_WHITESPACE +) +message("KWinEffect API Version: ${KWIN_EFFECT_API_VERSION_MINOR}") +message("") + +add_test (NAME KWinEffectSupport COMMAND sh ${CMAKE_CURRENT_LIST_DIR}/tools/isSupported.sh) +set_property (TEST KWinEffectSupport PROPERTY PASS_REGULAR_EXPRESSION "true") + + # these are cache variables, so they could be overwritten with -D, set(CPACK_PACKAGE_NAME kwin4-effect-shapecorners CACHE STRING kwin4-effect-shapecorners diff --git a/README.md b/README.md index b091ead..dc2931d 100644 --- a/README.md +++ b/README.md @@ -4,44 +4,66 @@ This effect rounds the corners of your windows and adds an outline around them w This effect started as a fork of [shapecorners](https://sourceforge.net/projects/shapecorners/) with some additional contributions in [Alex47's project](https://github.com/alex47/KDE-Rounded-Corners), then I optimized and reimplemented the effect with shaders with influences from the [invert effect](https://github.com/KDE/kwin/tree/master/src/plugins/invert). -#### Tested on: +**Tested on:** +- ![Wayland](https://img.shields.io/badge/Wayland-supported-green?logo=wayland) ![Wayland](https://img.shields.io/badge/X11-supported-green?logo=X.org) - ![Kubuntu 22.04 Jammy](https://img.shields.io/badge/-not_supported-red?label=Kubuntu%2022.04%20Jammy&logo=kubuntu&branch=master) -![](https://img.shields.io/badge/KWinEffects-v233-lightgrey) -- ![Kubuntu 22.04 Jammy + Backports](https://img.shields.io/github/actions/workflow/status/matinlotfali/KDE-Rounded-Corners/kubuntu2204-backports.yml?label=Kubuntu%2022.04%20Jammy%20+%20Backports&logo=kubuntu&branch=master) -![](https://img.shields.io/badge/KWinEffects-v234-lightgrey) +![](https://img.shields.io/badge/KWinEffects-v233-red) ![](https://img.shields.io/badge/Plasma-5.24-red) +- ![Kubuntu 22.04 Jammy + Backports](https://img.shields.io/github/actions/workflow/status/matinlotfali/KDE-Rounded-Corners/kubuntu2204-backports.yml?label=Kubuntu%2022.04%20Jammy%20%2b%20Backports&logo=kubuntu&branch=master) +![](https://img.shields.io/badge/KWinEffects-v236-lightgrey) ![](https://img.shields.io/badge/Plasma-5.27-lightgrey) - ![Debian 12 Bookworm](https://img.shields.io/github/actions/workflow/status/matinlotfali/KDE-Rounded-Corners/debian12.yml?branch=master&label=Debian%2012%20Bookworm&logo=debian) - ![](https://img.shields.io/badge/KWinEffects-v236-lightgrey) -- ![KDE Neon (Stable)](https://img.shields.io/github/actions/workflow/status/matinlotfali/KDE-Rounded-Corners/neon.yml?branch=master&label=KDE%20Neon%20%28Stable%29&logo=kde&logoColor=white) -![](https://img.shields.io/badge/KWinEffects-v236-lightgrey) + ![](https://img.shields.io/badge/KWinEffects-v236-lightgrey) ![](https://img.shields.io/badge/Plasma-5.27-lightgrey) +- ![KDE Neon (Stable)](https://img.shields.io/github/actions/workflow/status/matinlotfali/KDE-Rounded-Corners/neon.yml?branch=master&label=KDE%20Neon%20%28Stable%29&logo=kde&logoColor=white) +![](https://img.shields.io/badge/KWinEffects-v236-lightgrey) ![](https://img.shields.io/badge/Plasma-5.27-lightgrey) - ![KDE Neon (Unstable)](https://img.shields.io/github/actions/workflow/status/matinlotfali/KDE-Rounded-Corners/neon-unstable.yml?branch=master&label=KDE%20Neon%20%28Unstable%29&logo=kde&logoColor=white) -![](https://img.shields.io/badge/KWinEffects-v237-lightgrey) +![](https://img.shields.io/badge/KWinEffects-v237-lightgrey) ![](https://img.shields.io/badge/Plasma-6.0-lightgrey) - [![Arch](https://img.shields.io/github/actions/workflow/status/matinlotfali/KDE-Rounded-Corners/arch.yml?branch=master&label=Arch%20Linux&logo=archlinux&logoColor=white) ![](https://img.shields.io/aur/maintainer/kwin-effect-rounded-corners-git?label=AUR%20Maintainer) ![](https://img.shields.io/aur/votes/kwin-effect-rounded-corners-git?label=AUR%20Votes)](https://aur.archlinux.org/packages/kwin-effect-rounded-corners-git) -![After](screenshots/decoration-color.png) +![After](screenshots/shadows2.png) # Contributions: -- Compatibility of the effect with other effects like Wobbly windows - by [matinlotfali](https://github.com/matinlotfali) -- Optimize the effect to render once instead of 5 times - by [matinlotfali](https://github.com/matinlotfali) - see [#49](https://github.com/matinlotfali/KDE-Rounded-Corners/pull/49) -- Reimplementation with shaders to include shadows at corners and an outline - by [matinlotfali](https://github.com/matinlotfali) -- Compatibility with KWin from versions 5.23 to 5.27 - by [matinlotfali](https://github.com/matinlotfali) -- Disable effect when window gets maximized - by [matinlotfali](https://github.com/matinlotfali) +- Compatibility of the effect with other effects like Wobbly windows +- Compatibility with KWin for Plasma versions 5.27 to 6.0 +- Optimize the effect to render once instead of 5 times - see [#49](https://github.com/matinlotfali/KDE-Rounded-Corners/pull/49) +- Reimplementation with shaders to include shadows at corners and an outline +- Disable effect when windows get maximized - Cleanups for the plugin logic, remove unneeded dependencies from CMakeLists.txt file - by [alex1701c](https://github.com/alex1701c) - Separate outline color for active and inactive windows - by [OrkenWhite](https://github.com/OrkenWhite) +- Add natural shadows using parametric blend - by [paletteOvO](https://github.com/paletteOvO) # How to build from source code: You need to install development packages for your distribution first: -- **Debian based (Ubuntu, Kubuntu, KDE Neon)** - by [alex47](https://github.com/alex47): - ``` - sudo apt install git cmake g++ gettext extra-cmake-modules qttools5-dev libqt5x11extras5-dev libkf5configwidgets-dev libkf5globalaccel-dev libkf5notifications-dev kwin-dev - ``` -- **Fedora** - by [matinlotfali](https://github.com/matinlotfali) - ``` - sudo dnf install git cmake gcc-c++ extra-cmake-modules qt5-qttools-devel qt5-qttools-static qt5-qtx11extras-devel kf5-kconfigwidgets-devel kf5-kcrash-devel kf5-kguiaddons-devel kf5-kglobalaccel-devel kf5-kio-devel kf5-ki18n-devel kwin-devel qt5-qtbase-devel libepoxy-devel - ``` -- **Arch** - by [hexa-one](https://github.com/hexa-one) +
+Debian based (Ubuntu, Kubuntu, KDE Neon) +
+ + - Plasma 5 - by [alex47](https://github.com/alex47): + ``` + sudo apt install git cmake g++ extra-cmake-modules kwin-dev libkf5configwidgets-dev + ``` + - Plasma 6 + ``` + sudo apt install git cmake g++ extra-cmake-modules kwin-dev qt6-base-dev-tools kf6-kcmutils-dev + ``` +
+
+Fedora +
+ + - Plasma 5 (Fedora 39) + ```bash + sudo dnf install git cmake gcc-c++ extra-cmake-modules kwin-devel kf5-kconfigwidgets-devel libepoxy-devel + ``` + - Plasma 6 (Fedora 40 and later) + ```bash + sudo dnf install git cmake gcc-c++ extra-cmake-modules kwin-devel kf6-kconfigwidgets-devel libepoxy-devel kf6-kcmutils-devel qt6-qtbase-private-devel wayland-devel + ``` +
+
+Arch - by https://github.com/hexa-one + ``` sudo pacman -S git cmake extra-cmake-modules base-devel yay -S qt5-tools @@ -50,22 +72,31 @@ You need to install development packages for your distribution first: ``` sudo pamac build kwin-effect-rounded-corners-git ``` -- **OpenSUSE** - by [mathiasgredal](https://github.com/mathiasgredal) and [Richardsause](https://github.com/Richardsause) +
+
+OpenSUSE - by https://github.com/mathiasgredal and https://github.com/Richardsause + ``` - sudo zypper install git cmake gcc-c++ extra-cmake-modules libqt5-qttools-devel libqt5-qtx11extras-devel kconfigwidgets-devel kguiaddons-devel kglobalaccel-devel ki18n-devel knotifications-devel kwin5-devel libQt5Gui-devel libQt5OpenGL-devel libepoxy-devel kwindowsystem-devel libqt5-qtnetworkauth-devel + sudo zypper install git cmake gcc-c++ extra-cmake-modules libqt5-qttools-devel kconfigwidgets-devel kguiaddons-devel ki18n-devel knotifications-devel kwin5-devel libQt5Gui-devel libQt5OpenGL-devel libepoxy-devel libqt5-qtnetworkauth-devel ``` -- **Void** - by [lay-by](https://github.com/lay-by) +
+
+Void - by https://github.com/lay-by + ``` - xbps-install git cmake make qt5-tools-devel extra-cmake-modules qt5-x11extras-devel gettext-devel kwin-devel + xbps-install git cmake make qt5-tools-devel extra-cmake-modules gettext-devel kwin-devel ``` -- **NixOS** - by [Pavel Zolotarevskiy](https://github.com/flexagoon) +
+
+NixOS - by https://github.com/flexagoon + ``` nix-env -iA nixos.kde-rounded-corners ``` - +
-Then get the source code and compile: -``` +Then clone the source code and compile it: +```bash git clone https://github.com/matinlotfali/KDE-Rounded-Corners cd KDE-Rounded-Corners mkdir build @@ -75,15 +106,53 @@ make sudo make install ``` -You can now logout and log back in or run the command below to have it activated. +# Load & Unload + +To activate the effect, you can now log out and log back in, or run the command below inside the `build` directory: +```bash +sh ../tools/load.sh ``` -kwin --replace & + +To fully uninstall the effect, run the following commands inside the `build` directory: + +```bash +sh ../tools/unload.sh +sudo make uninstall +``` + +# Auto install after KWin update + +After each `kwin` package update, the effect becomes incompatible. So it won't load without a rebuild. + +As long as the effect is not part of the `kwin` yet (being discussed +[here](https://invent.kde.org/plasma/kwin/-/issues/198)), you can automate the re-installation by running the command +below inside the `build` directory: + +```bash +sh ../tools/autorun-test-install.sh ``` +The command above adds a `desktop` file inside the `autorun` directory which checks if the effect is still supported, +if it is not supported, it would automatically rebuild and reinstall the effect. + # Settings -You can change corner radius, or disable the effect in: +You can change the corner radius, or disable the effect in: + +> [ System Settings ] --> [ Workspace Behavior ] --> [ Desktop Effects ] --> [ ShapeCorners ] + +# Tips + +## Add shadow to windows without decoration (like Steam) + +You can add shadows for specific windows using the hack below. I don't know how to enforce it in my code. + +1. In [ System settings ] -> [ Window management ] -> [ Window rules ] -> [ Appearance & Fixes ]: + + **Add [steam] and set [ No titlebar ] and frame to [ No ]** + +3. In [ System settings ] -> [ Application Style ] -> [ Window decoration ] -> [ Breeze theme setting ] -> [ Window specific overrides ]: -> System Settings --> Workspace Behavior --> Desktop Effects --> ShapeCorners + **Add [steam] and set [ Hide Window title bar ] to [ Yes ].** -To fully uninstall, simply run the command `sudo make uninstall` inside `build` directory +After that, the Steam window gets its shadows back. diff --git a/screenshots/shadows2.png b/screenshots/shadows2.png new file mode 100644 index 0000000..0e60700 Binary files /dev/null and b/screenshots/shadows2.png differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4eafc81..7cfdbef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,29 +1,30 @@ add_subdirectory(kcm) set(effect_SRCS + ShapeCornersEffect.h ShapeCornersEffect.cpp + ShapeCornersShader.h ShapeCornersShader.cpp plugin.cpp ) kconfig_add_kcfg_files(effect_SRCS ShapeCornersConfig.kcfgc) -add_library(kwin4_effect_shapecorners SHARED ${effect_SRCS}) +kcoreaddons_add_plugin(kwin4_effect_shapecorners SOURCES ${effect_SRCS} INSTALL_NAMESPACE "kwin/effects/plugins/") target_link_libraries(kwin4_effect_shapecorners - Qt5::Core - Qt5::Gui - Qt5::DBus - KWinEffects::kwineffects - KWinEffects::kwinglutils - epoxy::epoxy - KF5::ConfigCore - KF5::ConfigGui - KF5::CoreAddons - KF5::WindowSystem + Qt${QT_MAJOR_VERSION}::DBus + KF${QT_MAJOR_VERSION}::ConfigGui ) +if(${QT_MAJOR_VERSION} EQUAL 6) + target_link_libraries(kwin4_effect_shapecorners + KWin::kwin + Qt${QT_MAJOR_VERSION}::CorePrivate + ) +else() + target_link_libraries(kwin4_effect_shapecorners + KWinEffects::kwineffects + KWinEffects::kwinglutils + ) +endif() -install(TARGETS kwin4_effect_shapecorners DESTINATION ${PLUGIN_INSTALL_DIR}/kwin/effects/plugins/) - -execute_process(COMMAND kf5-config --install data OUTPUT_VARIABLE DATAPATH OUTPUT_STRIP_TRAILING_WHITESPACE) -install(FILES shaders_140/shapecorners.frag DESTINATION ${DATAPATH}/kwin/shaders/1.40) -install(FILES shaders_110/shapecorners.frag DESTINATION ${DATAPATH}/kwin/shaders/1.10) \ No newline at end of file +install(DIRECTORY shaders DESTINATION ${KDE_INSTALL_DATADIR}/kwin) diff --git a/src/ShapeCornersEffect.cpp b/src/ShapeCornersEffect.cpp index 0b92b9c..3c0fd07 100644 --- a/src/ShapeCornersEffect.cpp +++ b/src/ShapeCornersEffect.cpp @@ -18,49 +18,43 @@ */ #include "ShapeCornersEffect.h" -#include +#include "ShapeCornersConfig.h" #include #include - -#if KWIN_EFFECT_API_VERSION >= 235 #include + +#if QT_VERSION_MAJOR >= 6 + #include + #include + #include + #include #else -#include + #include #endif ShapeCornersEffect::ShapeCornersEffect() -#if KWIN_EFFECT_API_VERSION >= 236 : KWin::OffscreenEffect() -#else - : KWin::DeformEffect() -#endif { reconfigure(ReconfigureAll); - auto connection = QDBusConnection::sessionBus(); - if (!connection.isConnected()) { + if (auto connection = QDBusConnection::sessionBus(); + !connection.isConnected()) { qWarning("ShapeCorners: Cannot connect to the D-Bus session bus.\n"); } else { - if (!connection.registerService("org.kde.ShapeCorners")) { + if (!connection.registerService(QStringLiteral("org.kde.ShapeCorners"))) { qWarning("%s\n", qPrintable(connection.lastError().message())); } else { - if (!connection.registerObject("/ShapeCornersEffect", this, QDBusConnection::ExportAllSlots)) { + if (!connection.registerObject(QStringLiteral("/ShapeCornersEffect"), this, QDBusConnection::ExportAllSlots)) { qWarning("%s\n", qPrintable(connection.lastError().message())); } } } if(m_shaderManager.IsValid()) { -#if KWIN_EFFECT_API_VERSION >= 235 - const auto& windowList = KX11Extras::windows(); -#else - const auto& windowList = KWindowSystem::windows(); -#endif - for (const auto& id: windowList) - if (auto win = KWin::effects->findWindow(id)) - windowAdded(win); + for (const auto& win: KWin::effects->stackingOrder()) + windowAdded(win); connect(KWin::effects, &KWin::EffectsHandler::windowAdded, this, &ShapeCornersEffect::windowAdded); connect(KWin::effects, &KWin::EffectsHandler::windowDeleted, this, &ShapeCornersEffect::windowRemoved); connect(KWin::effects, &KWin::EffectsHandler::windowFinishUserMovedResized, this, &ShapeCornersEffect::windowResized); @@ -74,8 +68,7 @@ void ShapeCornersEffect::windowAdded(KWin::EffectWindow *w) { qDebug() << w->windowRole() << w->windowType() << w->windowClass(); - auto [it, success] = m_managed.insert({w, false}); - if (success) { + if (const auto& [w2, r] = m_managed.insert({w, false}); r) { redirect(w); setShader(w, m_shaderManager.GetShader().get()); if (w->width() >= 300 && w->height() >= 300) @@ -92,17 +85,12 @@ void ShapeCornersEffect::windowRemoved(KWin::EffectWindow *w) } void -ShapeCornersEffect::reconfigure(ReconfigureFlags flags) +ShapeCornersEffect::reconfigure(const ReconfigureFlags flags) { Q_UNUSED(flags) ShapeCornersConfig::self()->read(); } -QRectF operator *(QRect r, qreal scale) { return {r.x() * scale, r.y() * scale, r.width() * scale, r.height() * scale}; } -QRectF operator *(QRectF r, qreal scale) { return {r.x() * scale, r.y() * scale, r.width() * scale, r.height() * scale}; } -QRect toRect(const QRectF& r) { return {(int)r.x(), (int)r.y(), (int)r.width(), (int)r.height()}; } -const QRect& toRect(const QRect& r) { return r; } - void ShapeCornersEffect::prePaintWindow(KWin::EffectWindow *w, KWin::WindowPrePaintData &data, std::chrono::milliseconds time) { if (!hasEffect(w) || (isTiled(w) && ShapeCornersConfig::disableRoundTile())) @@ -111,33 +99,24 @@ void ShapeCornersEffect::prePaintWindow(KWin::EffectWindow *w, KWin::WindowPrePa return; } - auto size = isWindowActive(w) ? ShapeCornersConfig::size(): ShapeCornersConfig::inactiveCornerRadius(); + const auto size = isWindowActive(w) ? ShapeCornersConfig::size(): ShapeCornersConfig::inactiveCornerRadius(); -#if KWIN_EFFECT_API_VERSION >= 234 - const auto geo_ex = w->expandedGeometry() * KWin::effects->renderTargetScale(); - const auto geo = w->frameGeometry() * KWin::effects->renderTargetScale(); +#if QT_VERSION_MAJOR >= 6 + const auto geo = w->frameGeometry() * w->screen()->scale(); data.setTranslucent(); #else - const auto& geo_ex = w->expandedGeometry(); - const auto& geo = w->expandedGeometry(); + const auto geo = w->frameGeometry() * KWin::effects->renderTargetScale(); + data.setTranslucent(); #endif QRegion reg {}; - reg += toRect(geo_ex); - reg -= toRect(geo); reg += QRect(geo.x(), geo.y(), size, size); reg += QRect(geo.x()+geo.width()-size, geo.y(), size, size); reg += QRect(geo.x(), geo.y()+geo.height()-size, size, size); reg += QRect(geo.x()+geo.width()-size, geo.y()+geo.height()-size, size, size); -#if KWIN_EFFECT_API_VERSION >= 234 data.opaque -= reg; -#endif data.paint += reg; -#if KWIN_EFFECT_API_VERSION >= 236 OffscreenEffect::prePaintWindow(w, data, time); -#else - DeformEffect::prePaintWindow(w, data, time); -#endif } bool ShapeCornersEffect::supported() @@ -145,45 +124,57 @@ bool ShapeCornersEffect::supported() return KWin::effects->isOpenGLCompositing(); } +#if QT_VERSION_MAJOR >= 6 +void ShapeCornersEffect::drawWindow(const KWin::RenderTarget &renderTarget, const KWin::RenderViewport &viewport, + KWin::EffectWindow *w, int mask, const QRegion ®ion, + KWin::WindowPaintData &data) { +#else void ShapeCornersEffect::drawWindow(KWin::EffectWindow *w, int mask, const QRegion ®ion, KWin::WindowPaintData &data) { +#endif if (!hasEffect(w)) { unredirect(w); -#if KWIN_EFFECT_API_VERSION >= 236 - OffscreenEffect::drawWindow(w, mask, region, data); +#if QT_VERSION_MAJOR >= 6 + OffscreenEffect::drawWindow(renderTarget, viewport, w, mask, region, data); #else - DeformEffect::drawWindow(w, mask, region, data); + OffscreenEffect::drawWindow(w, mask, region, data); #endif return; } +#if QT_VERSION_MAJOR >= 6 + const auto scale = viewport.scale(); +#else + const auto scale = KWin::effects->renderTargetScale(); +#endif + redirect(w); setShader(w, m_shaderManager.GetShader().get()); - m_shaderManager.Bind(w, isTiled(w)); + m_shaderManager.Bind(w, scale, isTiled(w)); glActiveTexture(GL_TEXTURE0); -#if KWIN_EFFECT_API_VERSION >= 236 - OffscreenEffect::drawWindow(w, mask, region, data); +#if QT_VERSION_MAJOR >= 6 + OffscreenEffect::drawWindow(renderTarget, viewport, w, mask, region, data); #else - DeformEffect::drawWindow(w, mask, region, data); + OffscreenEffect::drawWindow(w, mask, region, data); #endif m_shaderManager.Unbind(); } bool ShapeCornersEffect::hasEffect(const KWin::EffectWindow *w) const { - auto name = w->windowClass().split(' ').first(); + const auto name = w->windowClass().split(QChar::Space).first(); return m_shaderManager.IsValid() && m_managed.contains(w) - && (w->hasDecoration() || ShapeCornersConfig::inclusions().contains(name)) + && (w->isNormalWindow() || ShapeCornersConfig::inclusions().contains(name)) && !ShapeCornersConfig::exclusions().contains(name); } -QString ShapeCornersEffect::get_window_titles() { +QString ShapeCornersEffect::get_window_titles() const { QList response; for (const auto& [win, tiled]: m_managed) { - auto name = win->windowClass().split(' ').first(); - if (name == "plasmashell") + const auto name = win->windowClass().split(QChar::Space).first(); + if (name == QStringLiteral("plasmashell")) continue; if (!response.contains(name)) response.push_back(name); diff --git a/src/ShapeCornersEffect.h b/src/ShapeCornersEffect.h index 0302aee..21f0e19 100644 --- a/src/ShapeCornersEffect.h +++ b/src/ShapeCornersEffect.h @@ -19,18 +19,17 @@ #pragma once -#include -#include #include "ShapeCornersShader.h" -#define qDebugIfActive(w, s) if(ShapeCornersEffect::isWindowActive(w)) { qDebug() << s; } -#if KWIN_EFFECT_API_VERSION >= 236 -#include -class ShapeCornersEffect : public KWin::OffscreenEffect +#if QT_VERSION_MAJOR >= 6 + #include + #include #else -#include -class ShapeCornersEffect : public KWin::DeformEffect + #include + #include #endif + +class ShapeCornersEffect final: public KWin::OffscreenEffect { Q_OBJECT public: @@ -43,11 +42,19 @@ class ShapeCornersEffect : public KWin::DeformEffect void reconfigure(ReconfigureFlags flags) override; void prePaintWindow(KWin::EffectWindow *w, KWin::WindowPrePaintData &data, std::chrono::milliseconds time) override; - void drawWindow(KWin::EffectWindow *window, int mask, const QRegion ®ion, KWin::WindowPaintData &data) override; - int requestedEffectChainPosition() const override { return 99; } + +#if QT_VERSION_MAJOR >= 6 + void drawWindow(const KWin::RenderTarget &RenderTarget, const KWin::RenderViewport& viewport, + KWin::EffectWindow *w, int mask, const QRegion ®ion, KWin::WindowPaintData &data) override; +#else + void drawWindow(KWin::EffectWindow *w, int mask, const QRegion ®ion, KWin::WindowPaintData &data) override; +#endif + + [[nodiscard]] int requestedEffectChainPosition() const override { return 99; } + [[nodiscard]] bool blocksDirectScanout() const override { return false; } public Q_SLOTS: - QString get_window_titles(); + [[nodiscard]] QString get_window_titles() const; private Q_SLOTS: void windowAdded(KWin::EffectWindow *window); diff --git a/src/ShapeCornersShader.cpp b/src/ShapeCornersShader.cpp index 008e05c..42cfbf1 100644 --- a/src/ShapeCornersShader.cpp +++ b/src/ShapeCornersShader.cpp @@ -2,54 +2,46 @@ // Created by matin on 20/07/22. // -#include +#include "ShapeCornersShader.h" +#include "ShapeCornersEffect.h" +#include "ShapeCornersConfig.h" #include -#include -#include #include -#include "ShapeCornersEffect.h" -#include "ShapeCornersShader.h" + +#if QT_VERSION_MAJOR >= 6 + #include +#else + #include +#endif ShapeCornersShader::ShapeCornersShader(): m_manager(KWin::ShaderManager::instance()), - m_palette(QWidget().palette()) + m_widget(new QWidget) { - const QString shadersDir = IsLegacy()? "kwin/shaders/1.10/": "kwin/shaders/1.40/"; - const QString fragmentshader = QStandardPaths::locate(QStandardPaths::GenericDataLocation, shadersDir + QStringLiteral("shapecorners.frag")); + const QString fragmentshader = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/shaders/shapecorners.frag")); // m_shader = KWin::ShaderManager::instance()->loadFragmentShader(KWin::ShaderManager::GenericShader, fragmentshader); QFile file(fragmentshader); - if (file.open(QFile::ReadOnly)) - { - QByteArray frag = file.readAll(); - auto shader = m_manager->generateCustomShader(KWin::ShaderTrait::MapTexture, QByteArray(), frag); -#if KWIN_EFFECT_API_VERSION >= 235 - m_shader = std::move(shader); -#else - m_shader.reset(shader); -#endif - file.close(); -// qDebug() << frag; - if (m_shader->isValid()) - { - m_shader_disableRoundedTile = m_shader->uniformLocation("disableRoundedTile"); - m_shader_windowSize = m_shader->uniformLocation("windowSize"); - m_shader_windowExpandedSize = m_shader->uniformLocation("windowExpandedSize"); - m_shader_windowTopLeft = m_shader->uniformLocation("windowTopLeft"); - m_shader_shadowColor = m_shader->uniformLocation("shadowColor"); - m_shader_shadowSize = m_shader->uniformLocation("shadowSize"); - m_shader_radius = m_shader->uniformLocation("radius"); - m_shader_outlineColor = m_shader->uniformLocation("outlineColor"); - m_shader_outlineThickness = m_shader->uniformLocation("outlineThickness"); - m_shader_front = m_shader->uniformLocation("front"); - qInfo() << "ShapeCorners: shaders loaded."; - } - else - qCritical() << "ShapeCorners: no valid shaders found! ShapeCorners will not work."; - } - else - { + if (!file.open(QFile::ReadOnly)) qCritical() << "ShapeCorners: no shaders found! Exiting..."; - } + + const QByteArray frag = file.readAll(); + auto shader = m_manager->generateShaderFromFile(KWin::ShaderTrait::MapTexture, QStringLiteral(""), fragmentshader); + m_shader = std::move(shader); + file.close(); + if (!m_shader->isValid()) + qCritical() << "ShapeCorners: no valid shaders found! ShapeCorners will not work."; + + m_shader_disableRoundedTile = m_shader->uniformLocation("disableRoundedTile"); + m_shader_windowSize = m_shader->uniformLocation("windowSize"); + m_shader_windowExpandedSize = m_shader->uniformLocation("windowExpandedSize"); + m_shader_windowTopLeft = m_shader->uniformLocation("windowTopLeft"); + m_shader_shadowColor = m_shader->uniformLocation("shadowColor"); + m_shader_shadowSize = m_shader->uniformLocation("shadowSize"); + m_shader_radius = m_shader->uniformLocation("radius"); + m_shader_outlineColor = m_shader->uniformLocation("outlineColor"); + m_shader_outlineThickness = m_shader->uniformLocation("outlineThickness"); + m_shader_front = m_shader->uniformLocation("front"); + qInfo() << "ShapeCorners: shaders loaded."; } bool ShapeCornersShader::IsValid() const { @@ -57,20 +49,24 @@ bool ShapeCornersShader::IsValid() const { } const std::unique_ptr& -ShapeCornersShader::Bind(KWin::EffectWindow *w, bool isTiled) const { +ShapeCornersShader::Bind(KWin::EffectWindow *w, qreal scale, bool isTiled) const { QColor outlineColor, shadowColor; - auto xy = QVector2D((w->frameGeometry().left() - w->expandedGeometry().left()), - (w->frameGeometry().top() - w->expandedGeometry().top())); + qreal shadowSize; + auto& m_palette = m_widget->palette(); + auto frameGeometry = w->frameGeometry() * scale; + auto expandedGeometry = w->expandedGeometry() * scale; + auto xy = QVector2D(frameGeometry.topLeft() - expandedGeometry.topLeft()); + qreal max_shadow_size = xy.length(); m_manager->pushShader(m_shader.get()); - m_shader->setUniform(m_shader_windowSize, QVector2D(w->frameGeometry().width(), w->frameGeometry().height())); - m_shader->setUniform(m_shader_windowExpandedSize, QVector2D(w->expandedGeometry().width(), w->expandedGeometry().height())); + m_shader->setUniform(m_shader_windowSize, toVector2D(frameGeometry.size())); + m_shader->setUniform(m_shader_windowExpandedSize, toVector2D(expandedGeometry.size())); m_shader->setUniform(m_shader_windowTopLeft, xy); m_shader->setUniform(m_shader_disableRoundedTile, isTiled && ShapeCornersConfig::disableRoundTile()); m_shader->setUniform(m_shader_front, 0); if (ShapeCornersEffect::isWindowActive(w)) { - m_shader->setUniform(m_shader_shadowSize, (float)ShapeCornersConfig::shadowSize()); - m_shader->setUniform(m_shader_radius, (float)ShapeCornersConfig::size()); - m_shader->setUniform(m_shader_outlineThickness, (float)ShapeCornersConfig::outlineThickness()); + shadowSize = std::min(ShapeCornersConfig::shadowSize() * scale, max_shadow_size); + m_shader->setUniform(m_shader_radius, static_cast(ShapeCornersConfig::size() * scale)); + m_shader->setUniform(m_shader_outlineThickness, static_cast(ShapeCornersConfig::outlineThickness() * scale)); outlineColor = ShapeCornersConfig::activeOutlineUsePalette() ? m_palette.color(QPalette::Active, static_cast(ShapeCornersConfig::activeOutlinePalette())): @@ -81,19 +77,20 @@ ShapeCornersShader::Bind(KWin::EffectWindow *w, bool isTiled) const { outlineColor.setAlpha(isTiled && ShapeCornersConfig::disableOutlineTile() ? 0: ShapeCornersConfig::activeOutlineAlpha()); shadowColor.setAlpha(ShapeCornersConfig::activeShadowAlpha()); } else { - m_shader->setUniform(m_shader_shadowSize, (float)ShapeCornersConfig::inactiveShadowSize()); - m_shader->setUniform(m_shader_radius, (float)ShapeCornersConfig::inactiveCornerRadius()); - m_shader->setUniform(m_shader_outlineThickness, (float)ShapeCornersConfig::inactiveOutlineThickness()); + shadowSize = std::min(ShapeCornersConfig::inactiveShadowSize() * scale, max_shadow_size); + m_shader->setUniform(m_shader_radius, static_cast(ShapeCornersConfig::inactiveCornerRadius() * scale)); + m_shader->setUniform(m_shader_outlineThickness, static_cast(ShapeCornersConfig::inactiveOutlineThickness() * scale)); outlineColor = ShapeCornersConfig::inactiveOutlineUsePalette() ? - m_palette.color(QPalette::Active, static_cast(ShapeCornersConfig::inactiveOutlinePalette())): + m_palette.color(QPalette::Inactive, static_cast(ShapeCornersConfig::inactiveOutlinePalette())): ShapeCornersConfig::inactiveOutlineColor(); shadowColor = ShapeCornersConfig::inactiveShadowUsePalette() ? - m_palette.color(QPalette::Active, static_cast(ShapeCornersConfig::inactiveShadowPalette())): + m_palette.color(QPalette::Inactive, static_cast(ShapeCornersConfig::inactiveShadowPalette())): ShapeCornersConfig::inactiveShadowColor(); outlineColor.setAlpha(isTiled && ShapeCornersConfig::disableOutlineTile() ? 0: ShapeCornersConfig::inactiveOutlineAlpha()); shadowColor.setAlpha(ShapeCornersConfig::inactiveShadowAlpha()); } + m_shader->setUniform(m_shader_shadowSize, static_cast(shadowSize)); m_shader->setUniform(m_shader_outlineColor, outlineColor); m_shader->setUniform(m_shader_shadowColor, shadowColor); return m_shader; @@ -102,12 +99,3 @@ ShapeCornersShader::Bind(KWin::EffectWindow *w, bool isTiled) const { void ShapeCornersShader::Unbind() const { m_manager->popShader(); } - -bool ShapeCornersShader::IsLegacy() { -#ifdef KWIN_HAVE_OPENGLES - const qint64 coreVersionNumber = kVersionNumber(3, 0); -#else - const qint64 version = KWin::kVersionNumber(1, 40); -#endif - return (KWin::GLPlatform::instance()->glslVersion() < version); -} diff --git a/src/ShapeCornersShader.h b/src/ShapeCornersShader.h index 074a084..bab7268 100644 --- a/src/ShapeCornersShader.h +++ b/src/ShapeCornersShader.h @@ -5,39 +5,127 @@ #ifndef KWIN4_SHAPECORNERS_CONFIG_SHADERMANAGER_H #define KWIN4_SHAPECORNERS_CONFIG_SHADERMANAGER_H -#include -#include -#include -#include "ShapeCornersConfig.h" +#include +#if QT_VERSION_MAJOR >= 6 + #include +#else + #include +#endif namespace KWin { class GLShader; class ShaderManager; } +inline QRectF operator *(const QRect& r, const qreal scale) { return {r.x() * scale, r.y() * scale, r.width() * scale, r.height() * scale}; } +inline QRectF operator *(const QRectF& r, const qreal scale) { return {r.x() * scale, r.y() * scale, r.width() * scale, r.height() * scale}; } +inline QVector2D toVector2D(const QSizeF& s) { return {static_cast(s.width()), static_cast(s.height())}; } + class ShapeCornersShader { public: - ShapeCornersShader(); + /** + * \brief Loads the correct shader based on the GL platform in KWin. + * Then it assigns the location references for all its uniform variables so they could be written in. + */ + explicit ShapeCornersShader(); + + /** + * \return True if the shader is valid and loaded + */ + [[nodiscard]] bool IsValid() const; - static bool IsLegacy(); - bool IsValid() const; - const std::unique_ptr& Bind(KWin::EffectWindow *w, bool isTiled) const; + /** + * \brief This function assigns the required variables to the shader. + * Then it pushes the shader to the stack of rendering. + * This needs to be called before each window is rendered. + * \param w The window that the effect will be rendering on + * \return A reference to the unique pointer of the loaded shader. + */ + const std::unique_ptr& Bind(KWin::EffectWindow *w, qreal scale, bool isTiled) const; + + /** + * \brief Pop the shader from the stack of rendering. + * This needs to be called after each window is rendered. + */ void Unbind() const; + + /** + * \return A reference to the unique pointer of the loaded shader. + */ std::unique_ptr& GetShader() { return m_shader; } private: + /** + * \brief A pointer to the loaded shader used to assign the value of uniform variables. + */ std::unique_ptr m_shader; + + /** + * \brief An instance of `ShaderManager` used to load shader from file and push/pop the shader for each render. + */ KWin::ShaderManager* m_manager; - QPalette m_palette; + + /** + * \brief Used only for its `palette()` function which holds the currently active highlight colors. + */ + std::shared_ptr m_widget; + + /** + * \brief Reference to `uniform vec2 windowSize;` + * Containing `window->frameGeometry().size()` + */ int m_shader_windowSize = 0; + + /** + * \brief Reference to `uniform vec2 windowExpandedSize;` + * Containing `window->expandedGeometry().size()` + * \note When `expandedGeometry = frameGeometry`, it means there is no shadow. + */ int m_shader_windowExpandedSize = 0; + + /** + * \brief Reference to `uniform vec2 windowTopLeft;` + * Containing the distance between the top-left of `expandedGeometry` and the top-left of `frameGeometry`. + * \note When `windowTopLeft = {0,0}`, it means `expandedGeometry = frameGeometry` and there is no shadow. + */ int m_shader_windowTopLeft = 0; + int m_shader_disableRoundedTile = 0; + + /** + * \brief Reference to `uniform vec4 shadowColor;` + * Containing the RGBA of the shadow color specified in settings. + */ int m_shader_shadowColor = 0; + + /** + * \brief Reference to `uniform float shadowSize;` + * Containing the shadow size specified in settings. + */ int m_shader_shadowSize = 0; + + /** + * \brief Reference to `uniform float radius;` + * Containing the corner radius in pixels specified in settings. + */ int m_shader_radius = 0; + + /** + * \brief Reference to `uniform vec4 outlineColor;` + * Containing the RGBA of the outline color specified in settings. + */ int m_shader_outlineColor = 0; + + /** + * \brief Reference to `uniform float outlineThickness;` + * Containing the thickness of the outline in pixels specified in settings. + */ int m_shader_outlineThickness = 0; + + /** + * \brief Reference to `uniform sampler2D front;` + * Containing the active texture. When assigned to zero, it will contain the painted contents of the window. + */ int m_shader_front = 0; }; diff --git a/src/kcm/CMakeLists.txt b/src/kcm/CMakeLists.txt index 83c0247..d812930 100644 --- a/src/kcm/CMakeLists.txt +++ b/src/kcm/CMakeLists.txt @@ -1,24 +1,27 @@ set(kcm_SRCS + ShapeCornersKCM.h ShapeCornersKCM.cpp plugin.cpp ) kconfig_add_kcfg_files(kcm_SRCS ../ShapeCornersConfig.kcfgc) -qt5_wrap_ui(kcm_SRCS ShapeCornersKCM.ui) +if(${QT_MAJOR_VERSION} EQUAL 6) + qt6_wrap_ui(kcm_SRCS ShapeCornersKCM.ui) + qt6_add_dbus_interface(kcm_SRCS ${KWIN_EFFECTS_INTERFACE} kwineffects_interface) + kcoreaddons_add_plugin(kwin_shapecorners_config INSTALL_NAMESPACE "kwin/effects/configs" SOURCES ${kcm_SRCS}) -qt5_add_dbus_interface(kcm_SRCS ${KWIN_EFFECTS_INTERFACE} kwineffects_interface) - -add_library(kwin_shapecorners_config MODULE ${kcm_SRCS}) + target_link_libraries(kwin_shapecorners_config + Qt${QT_MAJOR_VERSION}::CorePrivate + KF${QT_MAJOR_VERSION}::KCMUtils + ) +else() + qt5_wrap_ui(kcm_SRCS ShapeCornersKCM.ui) + qt5_add_dbus_interface(kcm_SRCS ${KWIN_EFFECTS_INTERFACE} kwineffects_interface) + kcoreaddons_add_plugin(kwin_shapecorners_config INSTALL_NAMESPACE "kwin/effects/configs" SOURCES ${kcm_SRCS}) +endif () target_link_libraries(kwin_shapecorners_config - Qt5::Core - Qt5::DBus - Qt5::Gui - KF5::ConfigWidgets - KF5::ConfigGui - KF5::GlobalAccel - KF5::WindowSystem + Qt${QT_MAJOR_VERSION}::DBus + KF${QT_MAJOR_VERSION}::ConfigWidgets ) - -install(TARGETS kwin_shapecorners_config DESTINATION ${PLUGIN_INSTALL_DIR}/kwin/effects/configs) \ No newline at end of file diff --git a/src/kcm/ShapeCornersKCM.cpp b/src/kcm/ShapeCornersKCM.cpp index 8bfdf72..b644cc2 100644 --- a/src/kcm/ShapeCornersKCM.cpp +++ b/src/kcm/ShapeCornersKCM.cpp @@ -1,18 +1,26 @@ +#include "ShapeCornersKCM.h" +#include "ui_ShapeCornersKCM.h" +#include "kwineffects_interface.h" +#include "ShapeCornersConfig.h" #include #include -#include -#include "kwineffects_interface.h" -#include "ShapeCornersKCM.h" - -#include - +#if (QT_VERSION_MAJOR >= 6) +ShapeCornersKCM::ShapeCornersKCM(QObject* parent, const KPluginMetaData& args) + : KCModule(parent, args) + , ui(new Ui::Form) +{ + ui->setupUi(widget()); + addConfig(ShapeCornersConfig::self(), widget()); +#else ShapeCornersKCM::ShapeCornersKCM(QWidget* parent, const QVariantList& args) : KCModule(parent, args) , ui(new Ui::Form) { ui->setupUi(this); addConfig(ShapeCornersConfig::self(), this); +#endif + update_colors(); update_windows(); @@ -45,15 +53,22 @@ ShapeCornersKCM::ShapeCornersKCM(QWidget* parent, const QVariantList& args) connect(ui->kcfg_ActiveShadowUsePalette, &QRadioButton::toggled, this, &ShapeCornersKCM::update_colors); connect(ui->kcfg_InactiveShadowUsePalette, &QRadioButton::toggled, this, &ShapeCornersKCM::update_colors); + // It was expected that the Apply button would get enabled automatically as the gradient sliders move, but it doesn't. + // Maybe it is a bug on the KCM side. Need to check and delete these lines later. + connect(ui->kcfg_ActiveShadowAlpha, &KGradientSelector::sliderMoved, this, &ShapeCornersKCM::markAsChanged); + connect(ui->kcfg_InactiveShadowAlpha, &KGradientSelector::sliderMoved, this, &ShapeCornersKCM::markAsChanged); + connect(ui->kcfg_ActiveOutlineAlpha, &KGradientSelector::sliderMoved, this, &ShapeCornersKCM::markAsChanged); + connect(ui->kcfg_InactiveOutlineAlpha, &KGradientSelector::sliderMoved, this, &ShapeCornersKCM::markAsChanged); + connect(ui->refreshButton, &QPushButton::pressed, this, &ShapeCornersKCM::update_windows); connect(ui->includeButton, &QPushButton::pressed, [=, this]() { - auto s = ui->currentWindowList->currentItem(); - if (s && ui->InclusionList->findItems(s->text(), Qt::MatchExactly).empty()) + if (const auto s = ui->currentWindowList->currentItem(); + s && ui->InclusionList->findItems(s->text(), Qt::MatchExactly).empty()) ui->InclusionList->addItem(s->text()); }); connect(ui->excludeButton, &QPushButton::pressed, [=, this]() { - auto s = ui->currentWindowList->currentItem(); - if (s && ui->ExclusionList->findItems(s->text(), Qt::MatchExactly).empty()) + if (const auto s = ui->currentWindowList->currentItem(); + s && ui->ExclusionList->findItems(s->text(), Qt::MatchExactly).empty()) ui->ExclusionList->addItem(s->text()); }); connect(ui->deleteIncludeButton, &QPushButton::pressed, [=, this]() { @@ -77,14 +92,18 @@ ShapeCornersKCM::save() ShapeCornersConfig::setExclusions(exclusions); ShapeCornersConfig::self()->save(); KCModule::save(); + + const auto dbusName = QStringLiteral("kwin4_effect_shapecorners"); OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); - interface.reconfigureEffect(QStringLiteral("kwin4_effect_shapecorners")); -} + interface.reconfigureEffect(dbusName); -ShapeCornersKCM::~ShapeCornersKCM() { - delete ui; + // It was expected that the Apply button would repaint the whole screen, but it doesn't. + // Even calling KWin::effects->addRepaintFull(); didn't do the trick. + // Maybe it is a bug on the KWin side. Need to check and delete these lines later. + interface.unloadEffect(dbusName); + interface.loadEffect(dbusName); } void ShapeCornersKCM::update_colors() { @@ -113,23 +132,15 @@ void ShapeCornersKCM::update_colors() { ui->kcfg_InactiveShadowAlpha->setSecondColor(color); } -void ShapeCornersKCM::update_windows() { - QList windowList; - ui->currentWindowList->clear(); - - auto connection = QDBusConnection::sessionBus(); - if (connection.isConnected()) { - QDBusInterface interface("org.kde.ShapeCorners", "/ShapeCornersEffect"); - if (interface.isValid()) { - QDBusReply reply = interface.call("get_window_titles"); - if (reply.isValid()) - windowList = reply.value().split("\n"); - } - } +void ShapeCornersKCM::update_windows() const { + QStringList windowList; + if (const auto connection = QDBusConnection::sessionBus(); connection.isConnected()) + if (QDBusInterface interface(QStringLiteral("org.kde.ShapeCorners"), QStringLiteral("/ShapeCornersEffect")); interface.isValid()) + if (const QDBusReply reply = interface.call(QStringLiteral("get_window_titles")); reply.isValid()) + windowList = reply.value().split(QStringLiteral("\n")); - for (const auto& w: windowList) - if (!w.isEmpty()) - ui->currentWindowList->addItem(w); + ui->currentWindowList->clear(); + ui->currentWindowList->addItems(windowList); } void ShapeCornersKCM::load() { diff --git a/src/kcm/ShapeCornersKCM.h b/src/kcm/ShapeCornersKCM.h index fd71560..df06e99 100644 --- a/src/kcm/ShapeCornersKCM.h +++ b/src/kcm/ShapeCornersKCM.h @@ -1,22 +1,32 @@ - +#pragma once + #include -#include "ui_ShapeCornersKCM.h" -#include "ShapeCornersConfig.h" -class ShapeCornersKCM : public KCModule +namespace Ui { + class Form; +} + +class ShapeCornersKCM final: public KCModule { Q_OBJECT public: +#if (QT_VERSION_MAJOR >= 6) + explicit ShapeCornersKCM(QObject* parent, const KPluginMetaData& args); +#else explicit ShapeCornersKCM(QWidget* parent = nullptr, const QVariantList& args = QVariantList()); - ~ShapeCornersKCM() override; +#endif -public slots: +public Q_SLOTS: void defaults() override; void load() override; void save() override; void update_colors(); - void update_windows(); + void update_windows() const; private: - Ui::Form *ui; + std::shared_ptr ui; + +#if (QT_VERSION_MAJOR >= 6) + const QPalette& palette() { return widget()->palette(); }; +#endif }; diff --git a/src/kcm/ShapeCornersKCM.ui b/src/kcm/ShapeCornersKCM.ui index 725a9a8..8d5f62b 100644 --- a/src/kcm/ShapeCornersKCM.ui +++ b/src/kcm/ShapeCornersKCM.ui @@ -987,9 +987,28 @@ Inclusions && Exclusions - - - + + + + + + 0 + 0 + + + + Include > + + + + + + + + 0 + 0 + + 16777215 @@ -997,38 +1016,43 @@ - Excluded Windows: + List of Current Open Windows: - - - - QAbstractItemView::NoEditTriggers + + + + + 0 + 0 + - - false + + + 16777215 + 20 + - - - - - Exclude > + Included Windows: - - - - QAbstractItemView::NoEditTriggers + + + + + 0 + 0 + - - false + + < - + @@ -1041,8 +1065,14 @@ - - + + + + + 0 + 0 + + QAbstractItemView::NoEditTriggers @@ -1051,50 +1081,108 @@ - - + + + + + 0 + 0 + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 20 + + + + Excluded Windows: + + + + + + + Maximized Windows + + + + + + + + + + + 0 + 0 + + - Include > + Exclude > + + + 0 + 0 + + < - - - - - 16777215 - 20 - + + + + + 0 + 0 + - - List of Current Open Windows: + + QAbstractItemView::NoEditTriggers - - - - - - < + + false - - - - - 16777215 - 20 - + + + + + 0 + 0 + - - Included Windows: + + QAbstractItemView::NoEditTriggers + + + false diff --git a/src/kcm/plugin.cpp b/src/kcm/plugin.cpp index fcfb002..aae6c9a 100644 --- a/src/kcm/plugin.cpp +++ b/src/kcm/plugin.cpp @@ -3,8 +3,12 @@ // KF5 #include +#if (QT_VERSION_MAJOR >= 248) +K_PLUGIN_CLASS(ShapeCornersKCM) +#else K_PLUGIN_FACTORY_WITH_JSON(ShapeCornersConfigFactory, "metadata.json", registerPlugin();) +#endif #include "plugin.moc" \ No newline at end of file diff --git a/src/options.kcfg b/src/options.kcfg index 12b8d84..75c49c3 100644 --- a/src/options.kcfg +++ b/src/options.kcfg @@ -97,15 +97,15 @@ false + + + + true true - - - - \ No newline at end of file diff --git a/src/shaders/shapecorners.frag b/src/shaders/shapecorners.frag new file mode 100644 index 0000000..7e34920 --- /dev/null +++ b/src/shaders/shapecorners.frag @@ -0,0 +1,167 @@ +#version 110 + +uniform sampler2D front; // The painted contents of the window. +uniform float radius; // The thickness of the outline in pixels specified in settings. +uniform vec2 windowSize; // Containing `window->frameGeometry().size()` +uniform vec2 windowExpandedSize; // Containing `window->expandedGeometry().size()` + +uniform vec2 windowTopLeft; /* Containing the distance between the top-left of `expandedGeometry` and + * the top-left of `frameGeometry`. When `windowTopLeft = {0,0}`, it means + * `expandedGeometry = frameGeometry` and there is no shadow. */ + +uniform vec4 shadowColor; // The RGBA of the shadow color specified in settings. +uniform float shadowSize; // The shadow size specified in settings. +uniform vec4 outlineColor; // The RGBA of the outline color specified in settings. +uniform float outlineThickness; // The thickness of the outline in pixels specified in settings. + +uniform vec4 modulation; // This variable is assigned and used by KWinEffects used for proper fading. +uniform float saturation; // This variable is assigned and used by KWinEffects used for proper fading. + +varying vec2 texcoord0; // The XY location of the rendering pixel. Starting from {0.0, 0.0} to {1.0, 1.0} + +// Note: This version of GLSL uses the built-in variable `gl_FragColor` instead of `out vec4 fragColor;` + +vec2 tex_to_pixel(vec2 texcoord) { + return vec2(texcoord0.x * windowExpandedSize.x - windowTopLeft.x, + (1.0-texcoord0.y)* windowExpandedSize.y - windowTopLeft.y); +} +vec2 pixel_to_tex(vec2 pixelcoord) { + return vec2((pixelcoord.x + windowTopLeft.x) / windowExpandedSize.x, + 1.0-(pixelcoord.y + windowTopLeft.y) / windowExpandedSize.y); +} +bool isDrawingShadows() { return windowSize != windowExpandedSize && shadowColor.a > 0.0; } +bool isDrawingOutline() { + vec2 one_edge = vec2(windowSize.x/2.0, 0.0); + return texture2D(front, pixel_to_tex(one_edge)).a > 0.5 && + outlineColor.a > 0.0 && outlineThickness > 0.0; +} + +float parametricBlend(float t) { + float sqt = t * t; + return sqt / (2.0 * (sqt - t) + 1.0); +} + +/* + * \brief This function generates the shadow color based on the distance_from_center + * \param distance_from_center: Distance of the rendering point and the reference point that is being used for rounding corners. + * \return The RGBA color to be used for shadow. + */ +vec4 getShadowColor(float distance_from_center) { + if(!isDrawingShadows()) + return vec4(0.0, 0.0, 0.0, 0.0); + float percent = -distance_from_center/shadowSize + 1.0; + percent = clamp(percent, 0.0, 1.0); + percent = parametricBlend(percent); + if (percent < 0.0) + return vec4(0.0, 0.0, 0.0, 0.0); + else + return vec4(shadowColor.rgb * shadowColor.a * percent, shadowColor.a * percent); +} + +/* + * \brief This function is used to choose the pixel color based on its distance to the center input. + * \param coord0: The XY point + * \param tex: The RGBA color of the pixel in XY + * \param center: The origin XY point that is being used as a reference for rounding corners. + * \return The RGBA color to be used instead of tex input. + */ +vec4 shapeCorner(vec2 coord0, vec4 tex, vec2 start, float angle) { + bool diagonal = abs(cos(angle)) > 0.1 && abs(sin(angle)) > 0.1; + vec2 center; + float distance_from_center; + vec4 c; + float r; + if (disableRoundedTile) { + r = outlineThickness; + center = start + r * vec2(cos(angle), sin(angle)); + distance_from_center = distance(coord0, center); + c = tex; + } + else { + r = radius; + center = start + radius * (diagonal? sqrt(2.0) : 1.0) * vec2(cos(angle), sin(angle)); + distance_from_center = distance(coord0, center); + c = getShadowColor(distance_from_center); + } + + if(isDrawingOutline()) { + vec4 outlineOverlay = vec4(mix(tex.rgb, outlineColor.rgb, outlineColor.a), tex.a); + + if (distance_from_center < r - outlineThickness + 0.5) { + // from the window to the outline + float antialiasing = clamp(r-outlineThickness+0.5-distance_from_center, 0.0, 1.0); + return mix(outlineOverlay, tex, antialiasing); + } + else { + // from the outline to the shadow + float antialiasing = clamp(distance_from_center-r+0.5, 0.0, 1.0); + return mix(outlineOverlay, c, antialiasing); + } + } + else { + // from the window to the shadow + float antialiasing = clamp(r-distance_from_center, 0.0, 1.0); + return mix(c, tex, antialiasing); + } +} + +void main(void) +{ + vec4 tex = texture2D(front, texcoord0); // The RGBA of the XY pixel for the painted window + if(tex.a == 0.0) { + gl_FragColor = tex; + return; + } + + float r = disableRoundedTile? outlineThickness: radius; + + /* Since `texcoord0` is ranging from {0.0, 0.0} to {1.0, 1.0} is not pixel intuitive, + * I am changing the range to become from {0.0, 0.0} to {width, height} + * in a way that {0,0} is the top-left of the window and not its shadow. + * This means areas with negative numbers and areas beyond windowSize is considered part of the shadow. */ + vec2 coord0 = tex_to_pixel(texcoord0); + + /* + Split the window into these sections below. They will have a different center of circle for rounding. + + TL T T TR + L x x R + L x x R + BL B B BR + */ + if (coord0.y < r) { + if (coord0.x < r) + tex = shapeCorner(coord0, tex, vec2(0.0, 0.0), radians(45.0)); // Section TL + else if (coord0.x > windowSize.x - r) + tex = shapeCorner(coord0, tex, vec2(windowSize.x, 0.0), radians(135.0)); // Section TR + else if (coord0.y < outlineThickness) + tex = shapeCorner(coord0, tex, vec2(coord0.x, 0.0), radians(90.0)); // Section T + } + else if (coord0.y > windowSize.y - r) { + if (coord0.x < r) + tex = shapeCorner(coord0, tex, vec2(0.0, windowSize.y), radians(315.0)); // Section BL + else if (coord0.x > windowSize.x - r) + tex = shapeCorner(coord0, tex, vec2(windowSize.x, windowSize.y), radians(225.0)); // Section BR + else if (coord0.y > windowSize.y - outlineThickness) + tex = shapeCorner(coord0, tex, vec2(coord0.x, windowSize.y), radians(270.0)); // Section B + } + else { + if (coord0.x < r) + tex = shapeCorner(coord0, tex, vec2(0.0, coord0.y), radians(0.0)); // Section L + else if (coord0.x > windowSize.x - r) + tex = shapeCorner(coord0, tex, vec2(windowSize.x, coord0.y), radians(180.0)); // Section R + + // For section x, the tex is not changing + } + + // Apply the saturation and modulation. This is essential for proper fades in other KWin effects. + if (saturation != 1.0) { + vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 ); + desaturated = vec3( dot( desaturated, tex.rgb )); + tex.rgb = tex.rgb * vec3( saturation ) + desaturated * vec3( 1.0 - saturation ); + } + tex *= modulation; + + // Send to output + gl_FragColor = tex; +} diff --git a/src/shaders/shapecorners_core.frag b/src/shaders/shapecorners_core.frag new file mode 100644 index 0000000..33ef88c --- /dev/null +++ b/src/shaders/shapecorners_core.frag @@ -0,0 +1,167 @@ +#version 140 + +uniform sampler2D front; // The painted contents of the window. +uniform float radius; // The thickness of the outline in pixels specified in settings. +uniform vec2 windowSize; // Containing `window->frameGeometry().size()` +uniform vec2 windowExpandedSize; // Containing `window->expandedGeometry().size()` +uniform bool disableRoundedTile; + +uniform vec2 windowTopLeft; /* The distance between the top-left of `expandedGeometry` and + * the top-left of `frameGeometry`. When `windowTopLeft = {0,0}`, it means + * `expandedGeometry = frameGeometry` and there is no shadow. */ + +uniform vec4 shadowColor; // The RGBA of the shadow color specified in settings. +uniform float shadowSize; // The shadow size specified in settings. +uniform vec4 outlineColor; // The RGBA of the outline color specified in settings. +uniform float outlineThickness; // The thickness of the outline in pixels specified in settings. + +uniform vec4 modulation; // This variable is assigned and used by KWinEffects used for proper fading. +uniform float saturation; // This variable is assigned and used by KWinEffects used for proper fading. + +in vec2 texcoord0; // The XY location of the rendering pixel. Starting from {0.0, 0.0} to {1.0, 1.0} +out vec4 fragColor; // The RGBA color that can be rendered + +vec2 tex_to_pixel(vec2 texcoord) { + return vec2(texcoord0.x * windowExpandedSize.x - windowTopLeft.x, + (1.0-texcoord0.y)* windowExpandedSize.y - windowTopLeft.y); +} +vec2 pixel_to_tex(vec2 pixelcoord) { + return vec2((pixelcoord.x + windowTopLeft.x) / windowExpandedSize.x, + 1.0-(pixelcoord.y + windowTopLeft.y) / windowExpandedSize.y); +} +bool isDrawingShadows() { return windowSize != windowExpandedSize && shadowColor.a > 0.0; } +bool isDrawingOutline() { + vec2 one_edge = vec2(windowSize.x/2.0, 0.0); + return texture2D(front, pixel_to_tex(one_edge)).a > 0.5 && + outlineColor.a > 0.0 && outlineThickness > 0.0; +} + +float parametricBlend(float t) { + float sqt = t * t; + return sqt / (2.0 * (sqt - t) + 1.0); +} + +/* + * \brief This function generates the shadow color based on the distance_from_center + * \param distance_from_center: Distance of the rendering point and the reference point that is being used for rounding corners. + * \return The RGBA color to be used for shadow. + */ +vec4 getShadowColor(float distance_from_center) { + if(!isDrawingShadows()) + return vec4(0,0,0,0); + float percent = -distance_from_center/shadowSize + 1; + percent = clamp(percent, 0, 1); + percent = parametricBlend(percent); + if(percent < 0) + return vec4(0,0,0,0); + else + return vec4(shadowColor.rgb * shadowColor.a * percent, shadowColor.a * percent); +} + +/* + * \brief This function is used to choose the pixel color based on its distance to the center input. + * \param coord0: The XY point + * \param tex: The RGBA color of the pixel in XY + * \param center: The origin XY point that is being used as a reference for rounding corners. + * \return The RGBA color to be used instead of tex input. + */ +vec4 shapeCorner(vec2 coord0, vec4 tex, vec2 start, float angle) { + bool diagonal = abs(cos(angle)) > 0.1 && abs(sin(angle)) > 0.1; + vec2 center; + float distance_from_center; + vec4 c; + float r; + if (disableRoundedTile) { + r = outlineThickness; + center = start + r * vec2(cos(angle), sin(angle)); + distance_from_center = distance(coord0, center); + c = tex; + } + else { + r = radius; + center = start + radius * (diagonal? sqrt(2.0) : 1.0) * vec2(cos(angle), sin(angle)); + distance_from_center = distance(coord0, center); + c = getShadowColor(distance_from_center); + } + + if(isDrawingOutline()) { + vec4 outlineOverlay = vec4(mix(tex.rgb, outlineColor.rgb, outlineColor.a), tex.a); + + if (distance_from_center < r - outlineThickness + 0.5) { + // from the window to the outline + float antialiasing = clamp(r-outlineThickness+0.5-distance_from_center, 0.0, 1.0); + return mix(outlineOverlay, tex, antialiasing); + } + else { + // from the outline to the shadow + float antialiasing = clamp(distance_from_center-r+0.5, 0.0, 1.0); + return mix(outlineOverlay, c, antialiasing); + } + } + else { + // from the window to the shadow + float antialiasing = clamp(r-distance_from_center, 0.0, 1.0); + return mix(c, tex, antialiasing); + } +} + +void main(void) +{ + vec4 tex = texture(front, texcoord0); // The RGBA of the XY pixel for the painted window + if(tex.a == 0.0) { + fragColor = tex; + return; + } + + float r = disableRoundedTile? outlineThickness: radius; + + /* Since `texcoord0` is ranging from {0.0, 0.0} to {1.0, 1.0} is not pixel intuitive, + * I am changing the range to become from {0.0, 0.0} to {width, height} + * in a way that {0,0} is the top-left of the window and not its shadow. + * This means areas with negative numbers and areas beyond windowSize is considered part of the shadow. */ + vec2 coord0 = tex_to_pixel(texcoord0); + + /* + Split the window into these sections below. They will have a different center of circle for rounding. + + TL T T TR + L x x R + L x x R + BL B B BR + */ + if (coord0.y < r) { + if (coord0.x < r) + tex = shapeCorner(coord0, tex, vec2(0, 0), radians(45)); // Section TL + else if (coord0.x > windowSize.x - r) + tex = shapeCorner(coord0, tex, vec2(windowSize.x, 0), radians(135)); // Section TR + else if (coord0.y < outlineThickness) + tex = shapeCorner(coord0, tex, vec2(coord0.x, 0), radians(90)); // Section T + } + else if (coord0.y > windowSize.y - r) { + if (coord0.x < r) + tex = shapeCorner(coord0, tex, vec2(0, windowSize.y), radians(315)); // Section BL + else if (coord0.x > windowSize.x - r) + tex = shapeCorner(coord0, tex, vec2(windowSize.x, windowSize.y), radians(225)); // Section BR + else if (coord0.y > windowSize.y - outlineThickness) + tex = shapeCorner(coord0, tex, vec2(coord0.x, windowSize.y), radians(270)); // Section B + } + else { + if (coord0.x < r) + tex = shapeCorner(coord0, tex, vec2(0, coord0.y), radians(0)); // Section L + else if (coord0.x > windowSize.x - r) + tex = shapeCorner(coord0, tex, vec2(windowSize.x, coord0.y), radians(180)); // Section R + + // For section x, the tex is not changing + } + + // Apply the saturation and modulation. This is essential for proper fades in other KWin effects. + if (saturation != 1.0) { + vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 ); + desaturated = vec3( dot( desaturated, tex.rgb )); + tex.rgb = tex.rgb * vec3( saturation ) + desaturated * vec3( 1.0 - saturation ); + } + tex *= modulation; + + // Send to output + fragColor = tex; +} diff --git a/src/shaders_110/shapecorners.frag b/src/shaders_110/shapecorners.frag deleted file mode 100644 index f2572a9..0000000 --- a/src/shaders_110/shapecorners.frag +++ /dev/null @@ -1,107 +0,0 @@ -#version 110 - -uniform sampler2D front; -uniform float radius; -uniform bool disableRoundedTile; -uniform vec2 windowSize; -uniform vec2 windowExpandedSize; -uniform vec2 windowTopLeft; -uniform vec4 shadowColor; -uniform float shadowSize; -uniform vec4 outlineColor; -uniform float outlineThickness; -uniform vec4 modulation; -uniform float saturation; - -varying vec2 texcoord0; - -bool isDrawingShadows() { return windowSize != windowExpandedSize && shadowColor.a > 0.0; } -bool isDrawingOutline() { return outlineColor.a > 0.0 && outlineThickness > 0.0; } - -vec4 shadowCorner(float distance_from_center) { - float percent = -distance_from_center/shadowSize + 1.0; - if (percent < 0.0) - return vec4(0.0, 0.0, 0.0, 0.0); - else - return vec4(shadowColor.rgb * shadowColor.a * percent, shadowColor.a * percent); -} - -vec4 shapeCorner(vec2 coord0, vec4 tex, vec2 start, float angle) { - bool diagonal = abs(cos(angle)) > 0.1 && abs(sin(angle)) > 0.1; - - vec2 center; - float distance_from_center; - vec4 c; - float r; - - if (disableRoundedTile) { - r = outlineThickness; - center = start + r * vec2(cos(angle), sin(angle)); - distance_from_center = distance(coord0, center); - c = tex; - } - else { - r = radius; - center = start + radius * (diagonal? sqrt(2.0) : 1.0) * vec2(cos(angle), sin(angle)); - distance_from_center = distance(coord0, center); - c = isDrawingShadows() ? shadowCorner(distance_from_center) : vec4(0.0, 0.0, 0.0, 0.0); - } - - if(isDrawingOutline()) { - vec4 outlineOverlay = vec4(mix(tex.rgb, outlineColor.rgb, outlineColor.a), 1.0); - - if (distance_from_center < r - outlineThickness+0.5) { - float antialiasing = clamp(r-outlineThickness+0.5-distance_from_center, 0.0, 1.0); - return mix(outlineOverlay, tex, antialiasing); - } - else { - float antialiasing = clamp(distance_from_center-r+0.5, 0.0, 1.0); - return mix(outlineOverlay, c, antialiasing); - } - } - else { - float antialiasing = clamp(r-distance_from_center, 0.0, 1.0); - return mix(c, tex, antialiasing); - } -} - -void main(void) -{ - vec4 tex = texture2D(front, texcoord0); - vec2 coord0 = vec2(texcoord0.x * windowExpandedSize.x - windowTopLeft.x, - (1.0-texcoord0.y)* windowExpandedSize.y - windowTopLeft.y); - - float r = disableRoundedTile? outlineThickness: radius; - - if (coord0.y < r) { - if (coord0.x < r) - tex = shapeCorner(coord0, tex, vec2(0.0, 0.0), radians(45.0)); - else if (coord0.x > windowSize.x - r) - tex = shapeCorner(coord0, tex, vec2(windowSize.x, 0.0), radians(135.0)); - else if (coord0.y < outlineThickness) - tex = shapeCorner(coord0, tex, vec2(coord0.x, 0.0), radians(90.0)); - } - else if (coord0.y > windowSize.y - r) { - if (coord0.x < r) - tex = shapeCorner(coord0, tex, vec2(0.0, windowSize.y), radians(315.0)); - else if (coord0.x > windowSize.x - r) - tex = shapeCorner(coord0, tex, vec2(windowSize.x, windowSize.y), radians(225.0)); - else if (coord0.y > windowSize.y - outlineThickness) - tex = shapeCorner(coord0, tex, vec2(coord0.x, windowSize.y), radians(270.0)); - } - else { - if (coord0.x < radius) - tex = shapeCorner(coord0, tex, vec2(0.0, coord0.y), radians(0.0)); - else if (coord0.x > windowSize.x - radius) - tex = shapeCorner(coord0, tex, vec2(windowSize.x, coord0.y), radians(180.0)); - } - - if (saturation != 1.0) { - vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 ); - desaturated = vec3( dot( desaturated, tex.rgb )); - tex.rgb = tex.rgb * vec3( saturation ) + desaturated * vec3( 1.0 - saturation ); - } - tex *= modulation; - - gl_FragColor = tex; -} diff --git a/src/shaders_140/shapecorners.frag b/src/shaders_140/shapecorners.frag deleted file mode 100644 index 41df6c5..0000000 --- a/src/shaders_140/shapecorners.frag +++ /dev/null @@ -1,108 +0,0 @@ -#version 140 - -uniform sampler2D front; -uniform float radius; -uniform bool disableRoundedTile; -uniform vec2 windowSize; -uniform vec2 windowExpandedSize; -uniform vec2 windowTopLeft; -uniform vec4 shadowColor; -uniform float shadowSize; -uniform vec4 outlineColor; -uniform float outlineThickness; -uniform vec4 modulation; -uniform float saturation; - -in vec2 texcoord0; -out vec4 fragColor; - -bool isDrawingShadows() { return windowSize != windowExpandedSize && shadowColor.a > 0.0; } -bool isDrawingOutline() { return outlineColor.a > 0.0 && outlineThickness > 0.0; } - -vec4 shadowCorner(float distance_from_center) { - float percent = -distance_from_center/shadowSize + 1; - if(percent < 0) - return vec4(0,0,0,0); - else - return vec4(shadowColor.rgb * shadowColor.a * percent, shadowColor.a * percent); -} - -vec4 shapeCorner(vec2 coord0, vec4 tex, vec2 start, float angle) { - bool diagonal = abs(cos(angle)) > 0.1 && abs(sin(angle)) > 0.1; - - vec2 center; - float distance_from_center; - vec4 c; - float r; - - if (disableRoundedTile) { - r = outlineThickness; - center = start + r * vec2(cos(angle), sin(angle)); - distance_from_center = distance(coord0, center); - c = tex; - } - else { - r = radius; - center = start + radius * (diagonal? sqrt(2.0) : 1.0) * vec2(cos(angle), sin(angle)); - distance_from_center = distance(coord0, center); - c = isDrawingShadows() ? shadowCorner(distance_from_center) : vec4(0.0, 0.0, 0.0, 0.0); - } - - if(isDrawingOutline()) { - vec4 outlineOverlay = vec4(mix(tex.rgb, outlineColor.rgb, outlineColor.a), 1.0); - - if (distance_from_center < r - outlineThickness+0.5) { - float antialiasing = clamp(r-outlineThickness+0.5-distance_from_center, 0.0, 1.0); - return mix(outlineOverlay, tex, antialiasing); - } - else { - float antialiasing = clamp(distance_from_center-r+0.5, 0.0, 1.0); - return mix(outlineOverlay, c, antialiasing); - } - } - else { - float antialiasing = clamp(r-distance_from_center, 0.0, 1.0); - return mix(c, tex, antialiasing); - } -} - -void main(void) -{ - vec4 tex = texture(front, texcoord0); - vec2 coord0 = vec2(texcoord0.x * windowExpandedSize.x - windowTopLeft.x, - (1-texcoord0.y)* windowExpandedSize.y - windowTopLeft.y); - - float r = disableRoundedTile? outlineThickness: radius; - - if (coord0.y < r) { - if (coord0.x < r) - tex = shapeCorner(coord0, tex, vec2(0, 0), radians(45)); - else if (coord0.x > windowSize.x - r) - tex = shapeCorner(coord0, tex, vec2(windowSize.x, 0), radians(135)); - else if (coord0.y < outlineThickness) - tex = shapeCorner(coord0, tex, vec2(coord0.x, 0), radians(90)); - } - else if (coord0.y > windowSize.y - r) { - if (coord0.x < r) - tex = shapeCorner(coord0, tex, vec2(0, windowSize.y), radians(315)); - else if (coord0.x > windowSize.x - r) - tex = shapeCorner(coord0, tex, vec2(windowSize.x, windowSize.y), radians(225)); - else if (coord0.y > windowSize.y - outlineThickness) - tex = shapeCorner(coord0, tex, vec2(coord0.x, windowSize.y), radians(270)); - } - else { - if (coord0.x < radius) - tex = shapeCorner(coord0, tex, vec2(0, coord0.y), radians(0)); - else if (coord0.x > windowSize.x - radius) - tex = shapeCorner(coord0, tex, vec2(windowSize.x, coord0.y), radians(180)); - } - - if (saturation != 1.0) { - vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 ); - desaturated = vec3( dot( desaturated, tex.rgb )); - tex.rgb = tex.rgb * vec3( saturation ) + desaturated * vec3( 1.0 - saturation ); - } - tex *= modulation; - - fragColor = tex; -} diff --git a/tools/autorun-test-install.sh b/tools/autorun-test-install.sh new file mode 100644 index 0000000..ddb90ed --- /dev/null +++ b/tools/autorun-test-install.sh @@ -0,0 +1,6 @@ +AUTOSTART_FILE=~/.config/autostart/test-install-kde-rounded-corners.desktop +GIT_PATH="$(dirname "$(pwd)")" +mkdir -p ~/.config/autostart +cp "${GIT_PATH}"/tools/test-install.desktop ${AUTOSTART_FILE} +sed -i -e "s|Exec=|Exec=sh ${GIT_PATH}/|g" ${AUTOSTART_FILE} +sed -i -e "s|Path=|Path=${GIT_PATH}|g" ${AUTOSTART_FILE} \ No newline at end of file diff --git a/tools/isSupported.sh b/tools/isSupported.sh new file mode 100644 index 0000000..83934aa --- /dev/null +++ b/tools/isSupported.sh @@ -0,0 +1,2 @@ +dbus-send --dest=org.kde.KWin --print-reply /Effects \ + org.kde.kwin.Effects.isEffectSupported string:kwin4_effect_shapecorners | awk 'NR==2 {print $2}' \ No newline at end of file diff --git a/tools/load.sh b/tools/load.sh new file mode 100644 index 0000000..9129596 --- /dev/null +++ b/tools/load.sh @@ -0,0 +1,2 @@ +dbus-send --dest=org.kde.KWin --print-reply /Effects \ + org.kde.kwin.Effects.loadEffect string:kwin4_effect_shapecorners | awk 'NR==2 {print $2}' \ No newline at end of file diff --git a/tools/test-install.desktop b/tools/test-install.desktop new file mode 100644 index 0000000..e30e2ea --- /dev/null +++ b/tools/test-install.desktop @@ -0,0 +1,14 @@ +[Desktop Entry] +Comment= +Exec=tools/test-install.sh +GenericName= +MimeType= +Name=Test & Install KDE-Rounded-Corners +NoDisplay=false +Path=build/ +StartupNotify=true +Terminal=false +TerminalOptions= +Type=Application +X-KDE-SubstituteUID=false +X-KDE-Username= diff --git a/tools/test-install.sh b/tools/test-install.sh new file mode 100644 index 0000000..20da1a0 --- /dev/null +++ b/tools/test-install.sh @@ -0,0 +1,36 @@ +if ctest > /dev/null; then + echo "KDE-Rounded-Corners is supported by KWin and doesn't need re-installation." +else + kdialog --msgbox "KDE-Rounded-Corners is not supported by KWin anymore.\n\nThis can probably be for an update.\nWe will now rebuild and reinstall the effect." + make clean + cmake --build . -j & + pid=$! + + p=$(kdialog --progressbar "Building KDE-Rounded-Corners...") + qdbus $p showCancelButton false + + # Loop until the cmake --build process is complete + while kill -0 $pid 2>/dev/null; do + count=$(( $(tail -n 1 CMakeFiles/Progress/count.txt 2>/dev/null) + 2)) + if [ "$count" -gt "0" ]; then + value=$(find CMakeFiles/Progress/ | wc -l) + echo $value $count + qdbus $p Set "" value "$value" + qdbus $p Set "" maximum "$count" + fi + + # Sleep for a short duration to avoid excessive CPU usage + sleep 0.1 + done + qdbus $p close + + + kdialog --password "Enter password to install KDE-Rounded-Corners: " | sudo -S cmake --install . + + sh ../tools/load.sh + if ctest > /dev/null; then + kdialog --msgbox "KDE-Rounded-Corners is now installed and loaded." + else + kdialog --error "The installation was not successful.\nSee logs by compiling manually." + fi +fi \ No newline at end of file diff --git a/tools/unload.sh b/tools/unload.sh new file mode 100644 index 0000000..54874fc --- /dev/null +++ b/tools/unload.sh @@ -0,0 +1,2 @@ +dbus-send --dest=org.kde.KWin --print-reply /Effects \ + org.kde.kwin.Effects.unloadEffect string:kwin4_effect_shapecorners > /dev/null