diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 40503e480..6e6044477 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -25,3 +25,4 @@ add_subdirectory(test_idle_inhibit_v1) add_subdirectory(test_toplevel_tag) add_subdirectory(test_input_manager) add_subdirectory(test_keyboard_state_notify) +add_subdirectory(test_window_blur) diff --git a/examples/test_window_blur/CMakeLists.txt b/examples/test_window_blur/CMakeLists.txt new file mode 100644 index 000000000..932de42c0 --- /dev/null +++ b/examples/test_window_blur/CMakeLists.txt @@ -0,0 +1,27 @@ +find_package(Qt6 REQUIRED COMPONENTS Gui WaylandClient Widgets) +if(Qt6_VERSION VERSION_GREATER_EQUAL 6.10) + find_package(Qt6 REQUIRED COMPONENTS GuiPrivate WaylandClientPrivate) +endif() +find_package(TreelandProtocols REQUIRED) + +set(BIN_NAME test-window-blur) + +qt_add_executable(${BIN_NAME} + main.cpp +) + +qt_generate_wayland_protocol_client_sources(${BIN_NAME} + FILES + ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-personalization-manager-v1.xml +) + +target_link_libraries(${BIN_NAME} + PRIVATE + Qt6::Gui + Qt6::Widgets + Qt6::WaylandClient + Qt6::GuiPrivate + Qt6::WaylandClientPrivate +) + +install(TARGETS ${BIN_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/examples/test_window_blur/main.cpp b/examples/test_window_blur/main.cpp new file mode 100644 index 000000000..f872e9b03 --- /dev/null +++ b/examples/test_window_blur/main.cpp @@ -0,0 +1,390 @@ +// Copyright (C) 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwayland-treeland-personalization-manager-v1.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +class PersonalizationManager + : public QWaylandClientExtensionTemplate + , public QtWayland::treeland_personalization_manager_v1 +{ + Q_OBJECT +public: + explicit PersonalizationManager() + : QWaylandClientExtensionTemplate(1) + { + } +}; + +class PersonalizationWindow + : public QWaylandClientExtensionTemplate + , public QtWayland::treeland_personalization_window_context_v1 +{ + Q_OBJECT +public: + explicit PersonalizationWindow(struct ::treeland_personalization_window_context_v1 *object) + : QWaylandClientExtensionTemplate(1) + , QtWayland::treeland_personalization_window_context_v1(object) + { + } + + void destroyContext() + { + destroy(); + } +}; + +class BlurTestWindow : public QWidget +{ + Q_OBJECT +public: + BlurTestWindow() + : m_manager(new PersonalizationManager) + { + setWindowTitle("Window Blur Test Tool"); + setAttribute(Qt::WA_TranslucentBackground); + resize(640, 560); + + QVBoxLayout *mainLayout = new QVBoxLayout; + + m_statusLabel = new QLabel("Status: Waiting for personalization manager..."); + m_statusLabel->setStyleSheet("font-weight: bold; font-size: 14px; color: #2196F3;"); + mainLayout->addWidget(m_statusLabel); + + m_contextLabel = new QLabel("Context: null"); + m_contextLabel->setStyleSheet("font-size: 12px;"); + mainLayout->addWidget(m_contextLabel); + + m_blendLabel = new QLabel("Blend Mode: none"); + m_blendLabel->setStyleSheet("font-size: 12px;"); + mainLayout->addWidget(m_blendLabel); + + mainLayout->addWidget(createSeparator()); + + QLabel *manualLabel = new QLabel("Manual Controls"); + manualLabel->setStyleSheet("font-weight: bold; font-size: 13px;"); + mainLayout->addWidget(manualLabel); + + QHBoxLayout *contextLayout = new QHBoxLayout; + QPushButton *createCtxBtn = new QPushButton("Create Context"); + QPushButton *destroyCtxBtn = new QPushButton("Destroy Context"); + contextLayout->addWidget(createCtxBtn); + contextLayout->addWidget(destroyCtxBtn); + mainLayout->addLayout(contextLayout); + + connect(createCtxBtn, &QPushButton::clicked, this, &BlurTestWindow::createContext); + connect(destroyCtxBtn, &QPushButton::clicked, this, &BlurTestWindow::destroyContext); + + QHBoxLayout *blendLayout = new QHBoxLayout; + QPushButton *transparentBtn = new QPushButton("Set Transparent"); + QPushButton *wallpaperBtn = new QPushButton("Set Wallpaper"); + QPushButton *blurBtn = new QPushButton("Set Blur"); + blendLayout->addWidget(transparentBtn); + blendLayout->addWidget(wallpaperBtn); + blendLayout->addWidget(blurBtn); + mainLayout->addLayout(blendLayout); + + connect(transparentBtn, &QPushButton::clicked, this, [this]() { + setBlendMode(PersonalizationWindow::blend_mode_transparent); + }); + connect(wallpaperBtn, &QPushButton::clicked, this, [this]() { + setBlendMode(PersonalizationWindow::blend_mode_wallpaper); + }); + connect(blurBtn, &QPushButton::clicked, this, [this]() { + setBlendMode(PersonalizationWindow::blend_mode_blur); + }); + + mainLayout->addWidget(createSeparator()); + + QLabel *scenarioLabel = new QLabel("Test Scenarios"); + scenarioLabel->setStyleSheet("font-weight: bold; font-size: 13px;"); + mainLayout->addWidget(scenarioLabel); + + QPushButton *scenarioA = new QPushButton( + "A: Create + Blur -> Destroy (blur should disappear)"); + QPushButton *scenarioB = new QPushButton( + "B: Create + Blur (blur should show)"); + QPushButton *scenarioC = new QPushButton( + "C: Blur -> Destroy -> Create without blur (no blur)"); + QPushButton *scenarioD = new QPushButton( + "D: Blur -> Destroy -> Create + Transparent (switch correctly)"); + + scenarioA->setStyleSheet("text-align: left; padding: 6px;"); + scenarioB->setStyleSheet("text-align: left; padding: 6px;"); + scenarioC->setStyleSheet("text-align: left; padding: 6px;"); + scenarioD->setStyleSheet("text-align: left; padding: 6px;"); + + mainLayout->addWidget(scenarioA); + mainLayout->addWidget(scenarioB); + mainLayout->addWidget(scenarioC); + mainLayout->addWidget(scenarioD); + + connect(scenarioA, &QPushButton::clicked, this, &BlurTestWindow::runScenarioA); + connect(scenarioB, &QPushButton::clicked, this, &BlurTestWindow::runScenarioB); + connect(scenarioC, &QPushButton::clicked, this, &BlurTestWindow::runScenarioC); + connect(scenarioD, &QPushButton::clicked, this, &BlurTestWindow::runScenarioD); + + m_logLabel = new QLabel("Log: Ready"); + m_logLabel->setStyleSheet("font-size: 11px; color: #666; word-wrap: true;"); + m_logLabel->setWordWrap(true); + mainLayout->addWidget(m_logLabel); + + mainLayout->addStretch(); + setLayout(mainLayout); + + connect(m_manager, &PersonalizationManager::activeChanged, this, [this]() { + if (m_manager->isActive()) { + m_statusLabel->setText("Status: Personalization manager active"); + m_statusLabel->setStyleSheet("font-weight: bold; font-size: 14px; color: #4CAF50;"); + } + }); + } + + ~BlurTestWindow() + { + if (m_windowContext) { + m_windowContext->destroyContext(); + m_windowContext->deleteLater(); + m_windowContext = nullptr; + } + if (m_manager) { + delete m_manager; + m_manager = nullptr; + } + } + +private: + QFrame *createSeparator() + { + QFrame *line = new QFrame; + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + return line; + } + + void log(const QString &msg) + { + qDebug() << msg; + m_logLabel->setText("Log: " + msg); + } + + void createContext() + { + if (m_windowContext) { + log("Context already exists, destroy it first"); + return; + } + + QWindow *window = this->windowHandle(); + if (!window || !window->handle()) { + log("ERROR: Window handle not available"); + return; + } + + auto *waylandWindow = static_cast(window->handle()); + struct wl_surface *surface = waylandWindow->wlSurface(); + if (!surface) { + log("ERROR: wl_surface not available"); + return; + } + + m_windowContext = new PersonalizationWindow(m_manager->get_window_context(surface)); + m_contextLabel->setText("Context: active"); + m_blendLabel->setText("Blend Mode: none (not set)"); + log("Context created"); + } + + void destroyContext() + { + if (!m_windowContext) { + log("No context to destroy"); + return; + } + + m_windowContext->destroyContext(); + m_windowContext->deleteLater(); + m_windowContext = nullptr; + m_contextLabel->setText("Context: null"); + m_blendLabel->setText("Blend Mode: context destroyed"); + log("Context destroyed - blur should disappear if it was set"); + } + + void setBlendMode(int mode) + { + if (!m_windowContext) { + log("ERROR: No context, create one first"); + return; + } + + m_windowContext->set_blend_mode(mode); + + QString modeName; + switch (mode) { + case PersonalizationWindow::blend_mode_transparent: + modeName = "transparent"; + break; + case PersonalizationWindow::blend_mode_wallpaper: + modeName = "wallpaper"; + break; + case PersonalizationWindow::blend_mode_blur: + modeName = "blur"; + break; + default: + modeName = "unknown"; + break; + } + + m_blendLabel->setText("Blend Mode: " + modeName); + log("Set blend mode: " + modeName); + } + + void runScenarioA() + { + if (m_scenarioRunning) { + log("Scenario already running, wait for completion"); + return; + } + m_scenarioRunning = true; + log("Scenario A: Create + Blur -> Destroy"); + + if (m_windowContext) { + destroyContext(); + } + + createContext(); + + QTimer::singleShot(500, this, [this]() { + setBlendMode(PersonalizationWindow::blend_mode_blur); + log("Scenario A: Blur set, destroying context in 2s..."); + + QTimer::singleShot(2000, this, [this]() { + destroyContext(); + log("Scenario A: Context destroyed. Check: blur should have disappeared."); + m_scenarioRunning = false; + }); + }); + } + + void runScenarioB() + { + if (m_scenarioRunning) { + log("Scenario already running, wait for completion"); + return; + } + m_scenarioRunning = true; + log("Scenario B: Create + Blur (normal)"); + + if (m_windowContext) { + destroyContext(); + } + + createContext(); + + QTimer::singleShot(500, this, [this]() { + setBlendMode(PersonalizationWindow::blend_mode_blur); + log("Scenario B: Blur should be visible now."); + m_scenarioRunning = false; + }); + } + + void runScenarioC() + { + if (m_scenarioRunning) { + log("Scenario already running, wait for completion"); + return; + } + m_scenarioRunning = true; + log("Scenario C: Blur -> Destroy -> Create without blur"); + + if (m_windowContext) { + destroyContext(); + } + + createContext(); + + QTimer::singleShot(500, this, [this]() { + setBlendMode(PersonalizationWindow::blend_mode_blur); + log("Scenario C: Blur set, destroying context in 2s..."); + + QTimer::singleShot(2000, this, [this]() { + destroyContext(); + log("Scenario C: Context destroyed. Creating new context without blur in 1s..."); + + QTimer::singleShot(1000, this, [this]() { + createContext(); + log("Scenario C: New context created without setting blur. Check: no blur should be visible."); + m_scenarioRunning = false; + }); + }); + }); + } + + void runScenarioD() + { + if (m_scenarioRunning) { + log("Scenario already running, wait for completion"); + return; + } + m_scenarioRunning = true; + log("Scenario D: Blur -> Destroy -> Create + Transparent"); + + if (m_windowContext) { + destroyContext(); + } + + createContext(); + + QTimer::singleShot(500, this, [this]() { + setBlendMode(PersonalizationWindow::blend_mode_blur); + log("Scenario D: Blur set, destroying context in 2s..."); + + QTimer::singleShot(2000, this, [this]() { + destroyContext(); + log("Scenario D: Context destroyed. Creating new context with transparent in 1s..."); + + QTimer::singleShot(1000, this, [this]() { + createContext(); + + QTimer::singleShot(500, this, [this]() { + setBlendMode(PersonalizationWindow::blend_mode_transparent); + log("Scenario D: New context with transparent mode. Check: blur should not be visible, window should be transparent."); + m_scenarioRunning = false; + }); + }); + }); + }); + } + + PersonalizationManager *m_manager = nullptr; + PersonalizationWindow *m_windowContext = nullptr; + bool m_scenarioRunning = false; + + QLabel *m_statusLabel = nullptr; + QLabel *m_contextLabel = nullptr; + QLabel *m_blendLabel = nullptr; + QLabel *m_logLabel = nullptr; +}; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + BlurTestWindow window; + window.show(); + + return app.exec(); +} + +#include "main.moc" diff --git a/src/modules/personalization/personalizationmanagerinterfacev1.cpp b/src/modules/personalization/personalizationmanagerinterfacev1.cpp index 3466169e1..f297a18ed 100644 --- a/src/modules/personalization/personalizationmanagerinterfacev1.cpp +++ b/src/modules/personalization/personalizationmanagerinterfacev1.cpp @@ -1022,64 +1022,73 @@ Personalization::Personalization(WToplevelSurface *target, , m_target(target) , m_manager(manager) { - connect(target, &WToplevelSurface::aboutToBeInvalidated, this, [this] { - disconnect(m_connection); - }); + connect(target, &WToplevelSurface::aboutToBeInvalidated, this, &QObject::deleteLater); - auto update = [this](PersonalizationWindowContextV1 *context) { - assert(context); + connect(m_manager, &PersonalizationManagerInterfaceV1::windowContextCreated, this, &Personalization::updateFromContext); - if (WSurface::fromHandle(context->surface()) != m_target->surface()) { - return; - } + if (auto *context = PersonalizationWindowContextV1::getWindowContext(m_target->surface())) { + updateFromContext(context); + } +} + +void Personalization::updateFromContext(PersonalizationWindowContextV1 *context) +{ + assert(context); + + if (WSurface::fromHandle(context->surface()) != m_target->surface()) { + return; + } + + if (m_currentContext) { + disconnect(m_currentContext, nullptr, this, nullptr); + } + + m_currentContext = context; + + connect(context, + &PersonalizationWindowContextV1::backgroundTypeChanged, + this, + [this, context] { + m_backgroundType = context->backgroundType(); + Q_EMIT backgroundTypeChanged(); + }); + connect(context, + &PersonalizationWindowContextV1::cornerRadiusChanged, + this, + [this, context] { + m_cornerRadius = context->cornerRadius(); + Q_EMIT cornerRadiusChanged(); + }); - disconnect(m_connection); - - connect(context, - &PersonalizationWindowContextV1::backgroundTypeChanged, - this, - [this, context] { - m_backgroundType = context->backgroundType(); - Q_EMIT backgroundTypeChanged(); - }); - connect(context, - &PersonalizationWindowContextV1::cornerRadiusChanged, - this, - [this, context] { - m_cornerRadius = context->cornerRadius(); - Q_EMIT cornerRadiusChanged(); - }); - - connect(context, &PersonalizationWindowContextV1::shadowChanged, this, [this, context] { - m_shadow = context->shadow(); - Q_EMIT shadowChanged(); - }); - - connect(context, &PersonalizationWindowContextV1::borderChanged, this, [this, context] { - m_border = context->border(); - Q_EMIT borderChanged(); - }); - - connect(context, - &PersonalizationWindowContextV1::windowStateChanged, - this, - [this, context] { - m_states = context->states(); - Q_EMIT windowStateChanged(); - }); - - m_backgroundType = context->backgroundType(); - m_cornerRadius = context->cornerRadius(); + connect(context, &PersonalizationWindowContextV1::shadowChanged, this, [this, context] { m_shadow = context->shadow(); + Q_EMIT shadowChanged(); + }); + + connect(context, &PersonalizationWindowContextV1::borderChanged, this, [this, context] { m_border = context->border(); - m_states = context->states(); - }; + Q_EMIT borderChanged(); + }); - m_connection = connect(m_manager, &PersonalizationManagerInterfaceV1::windowContextCreated, this, update); + connect(context, + &PersonalizationWindowContextV1::windowStateChanged, + this, + [this, context] { + m_states = context->states(); + Q_EMIT windowStateChanged(); + }); - if (auto *context = PersonalizationWindowContextV1::getWindowContext(m_target->surface())) { - update(context); - } + m_backgroundType = context->backgroundType(); + m_cornerRadius = context->cornerRadius(); + m_shadow = context->shadow(); + m_border = context->border(); + m_states = context->states(); + + Q_EMIT backgroundTypeChanged(); + Q_EMIT cornerRadiusChanged(); + Q_EMIT shadowChanged(); + Q_EMIT borderChanged(); + Q_EMIT windowStateChanged(); } SurfaceWrapper *Personalization::surfaceWrapper() const diff --git a/src/modules/personalization/personalizationmanagerinterfacev1.h b/src/modules/personalization/personalizationmanagerinterfacev1.h index 0aef77e66..0b5f1bd62 100644 --- a/src/modules/personalization/personalizationmanagerinterfacev1.h +++ b/src/modules/personalization/personalizationmanagerinterfacev1.h @@ -242,15 +242,16 @@ class Personalization : public QObject void windowStateChanged(); private: + void updateFromContext(PersonalizationWindowContextV1 *context); + WWrapPointer m_target; PersonalizationManagerInterfaceV1 *m_manager = nullptr; + QPointer m_currentContext; int32_t m_backgroundType = Personalization::BackgroundType::Normal; int32_t m_cornerRadius = 0; Shadow m_shadow {}; Border m_border {}; PersonalizationWindowContextV1::WindowStates m_states {}; - - QMetaObject::Connection m_connection; }; class PersonalizationManagerInterfaceV1 : public QObject, public WServerInterface