diff --git a/src/fade/fade-xrandr.cpp b/src/fade/fade-xrandr.cpp index 0643f02ee910615e20e09c3c82fd74589a61dc45..cb9e4fb6df95eacb9412b9335b00f5a69e7276b3 100644 --- a/src/fade/fade-xrandr.cpp +++ b/src/fade/fade-xrandr.cpp @@ -13,7 +13,7 @@ */ #include "fade-xrandr.h" -#include "xcbutils.h" +#include "xcb-randr-utils.h" #include #include diff --git a/src/grab/grab.cpp b/src/grab/grab.cpp index 92282d1bba7fbb1a53cb86ccda2eb0c73fd6eef5..bab4386da94f922b8483780fe4dc2df0168c7caa 100644 --- a/src/grab/grab.cpp +++ b/src/grab/grab.cpp @@ -14,7 +14,7 @@ #include "grab.h" #include "invisible-window.h" - +#include "xcb-utils.h" #include #include #include @@ -27,33 +27,6 @@ using namespace Kiran::ScreenSaver; -struct FreeDeleter -{ - void operator()(void* pointer) const Q_DECL_NOTHROW - { - return std::free(pointer); - } -}; - -#define XCB_REPLY_CONNECTION_ARG(connection, ...) connection - -#define KS_XCB_REPLY(call, ...) \ - std::unique_ptr(call##_reply(XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call(__VA_ARGS__), nullptr)) - -class XServerGrabber -{ -public: - XServerGrabber() - { - xcb_grab_server(QX11Info::connection()); - } - ~XServerGrabber() - { - xcb_ungrab_server(QX11Info::connection()); - xcb_flush(QX11Info::connection()); - } -}; - Grab* Grab::getInstance() { static QMutex mutex; diff --git a/src/tools/xcbutils.h b/src/tools/xcb-randr-utils.h similarity index 100% rename from src/tools/xcbutils.h rename to src/tools/xcb-randr-utils.h diff --git a/src/tools/xcb-utils.h b/src/tools/xcb-utils.h new file mode 100644 index 0000000000000000000000000000000000000000..535a4e3c5bfe7b6c0a2abfb62cc660b24c830b1c --- /dev/null +++ b/src/tools/xcb-utils.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2020 ~ 2024 KylinSec Co., Ltd. + * kiran-screensaver is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#pragma once +#include +#include +#include + +struct FreeDeleter +{ + void operator()(void* pointer) const Q_DECL_NOTHROW + { + return std::free(pointer); + } +}; + +#define XCB_REPLY_CONNECTION_ARG(connection, ...) connection + +#define KS_XCB_REPLY(call, ...) \ + std::unique_ptr(call##_reply(XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call(__VA_ARGS__), nullptr)) + +class XServerGrabber +{ +public: + XServerGrabber() + { + xcb_grab_server(QX11Info::connection()); + } + ~XServerGrabber() + { + xcb_ungrab_server(QX11Info::connection()); + xcb_flush(QX11Info::connection()); + } +}; \ No newline at end of file diff --git a/src/view/screen-manager.cpp b/src/view/screen-manager.cpp index 288278e59fe1b3145c682e2e2e9c89b1723bdf62..fb1f2b1a3051febf480c15ae37efefa554fbb88e 100644 --- a/src/view/screen-manager.cpp +++ b/src/view/screen-manager.cpp @@ -81,6 +81,14 @@ ScreenManager::ScreenManager(Fade *fade, QApplication::instance()->installEventFilter(this); m_visibilityMonitor = VisibilityMonitor::instance(); + connect(m_visibilityMonitor, &VisibilityMonitor::restackedNeedRaise, + [this]() + { + for (auto iter : m_windowMap) + { + iter->raiseDelay(); + } + }); connect(m_visibilityMonitor, &VisibilityMonitor::visibilityStateChanged, [this](WId wid, VisibilityMonitor::VisibilityState state) { @@ -271,7 +279,7 @@ Window *ScreenManager::createWindowForScreen(QScreen *screen) window->setScreen(screen); window->setBackground(m_background); - m_visibilityMonitor->monitorWindow(window->winId()); + m_visibilityMonitor->monitor(window->winId()); window->show(); window->raise(); @@ -300,7 +308,7 @@ void ScreenManager::deleteWindowForScreen(QScreen *screen) moveContentToWindow(newScreenBackground); m_currentWindow = newScreenBackground; } - + m_visibilityMonitor->unmonitor(screenBackground->winId()); delete screenBackground; } } @@ -386,6 +394,7 @@ void ScreenManager::destroyWindows() for (auto iter = m_windowMap.begin(); iter != m_windowMap.end();) { + m_visibilityMonitor->unmonitor(iter.value()->winId()); delete iter.value(); m_windowMap.erase(iter++); } diff --git a/src/view/visibility-monitor.cpp b/src/view/visibility-monitor.cpp index 71af1d5913e741ae43b35e6033882fe87f6dee9a..a8d851d5608a9ffeb3890f773de0c976dcc49126 100644 --- a/src/view/visibility-monitor.cpp +++ b/src/view/visibility-monitor.cpp @@ -17,6 +17,7 @@ #include #include #include +#include "xcb-utils.h" namespace Kiran { @@ -47,11 +48,47 @@ VisibilityMonitor::~VisibilityMonitor() } } -void VisibilityMonitor::monitorWindow(WId wid) +void VisibilityMonitor::monitor(WId wid) { + if (m_windows.contains(wid)) + { + return; + } + KLOG_INFO() << "visibility monitor:" << wid; + + // 关闭混成时,XServer发出VisibilityNotify事件 + // 直接订阅该事件用来监控窗口可见状态更高效 uint32_t valueList[] = {XCB_EVENT_MASK_VISIBILITY_CHANGE}; xcb_change_window_attributes(m_xcbConnection, wid, XCB_CW_EVENT_MASK, valueList); + + // 订阅用来处理混成顺序的事件 + // ConfigureNotify 以及 MapNotify + if (m_windows.isEmpty()) + { + selectSubstructureNotify(); + } + xcb_flush(m_xcbConnection); + m_windows << wid; +} + +void VisibilityMonitor::unmonitor(WId wid) +{ + if (!m_windows.contains(wid)) + { + return; + } + + KLOG_INFO() << "visibility unmonitor:" << wid; + + uint32_t valueList[] = {XCB_EVENT_MASK_NO_EVENT}; + xcb_change_window_attributes(m_xcbConnection, wid, XCB_CW_EVENT_MASK, valueList); + + m_windows.remove(wid); + if (m_windows.isEmpty()) + { + unselectSubstructureNotify(); + } xcb_flush(m_xcbConnection); } @@ -85,6 +122,64 @@ void VisibilityMonitor::init() m_inited = true; } +bool VisibilityMonitor::isInternal(WId window) +{ + bool bRes = false; + if (m_windows.contains(window) && window != QX11Info::appRootWindow()) + { + bRes = true; + } + return bRes; +} + +void VisibilityMonitor::selectSubstructureNotify() +{ + WId root = QX11Info::appRootWindow(); + + auto reply = KS_XCB_REPLY(xcb_get_window_attributes, m_xcbConnection, root); + if (reply == nullptr) + { + KLOG_WARNING() << "select SubstructureNotify failed,can't get root window attributes"; + return; + } + if (reply->your_event_mask & XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY) + { + KLOG_DEBUG() << "SubstructureNotify already selected"; + return; + } + + uint32_t values[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY}; + // uint32_t values[] = {XCB_EVENT_MASK_VISIBILITY_CHANGE}; + xcb_change_window_attributes(m_xcbConnection, root, + XCB_CW_EVENT_MASK, values); + + KLOG_INFO() << "select root window substructure notify event."; +} + +void VisibilityMonitor::unselectSubstructureNotify() +{ + WId root = QX11Info::appRootWindow(); + + auto reply = KS_XCB_REPLY(xcb_get_window_attributes, m_xcbConnection, root); + if (reply == nullptr) + { + KLOG_WARNING() << "unselect SubstructureNotify failed,can't get root window attributes"; + return; + } + + if (!(reply->your_event_mask & XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY)) + { + KLOG_DEBUG() << "SubstructureNotify already unselected"; + return; + } + + uint32_t values[] = {XCB_EVENT_MASK_NO_EVENT}; + xcb_change_window_attributes(m_xcbConnection, root, + XCB_CW_EVENT_MASK, values); + + KLOG_INFO() << "unselect root window substructure notify event."; +} + void VisibilityMonitor::handleXcbEvent() { int count = 0; @@ -93,36 +188,70 @@ void VisibilityMonitor::handleXcbEvent() switch (event->response_type & ~0x80) { case XCB_VISIBILITY_NOTIFY: - { - xcb_visibility_notify_event_t* visibilityEvent = reinterpret_cast(event); - uint8_t visibilityState = visibilityEvent->state; - - if ((visibilityState >= VISIBILITY_UNOBSCURED) && (visibilityState < VISIBILITY_LAST)) - { - VisibilityState eState = (VisibilityState)visibilityState; - KLOG_INFO() << "visibility state changed:" - << visibilityEvent->window - << "->" << eState; - emit visibilityStateChanged(visibilityEvent->window,eState); - } - else - { - KLOG_WARNING() << "unknow visibility state:" << visibilityEvent->window << visibilityState; - } + onVisibilityNotify(event); + break; + case XCB_MAP_NOTIFY: + onMapNotify(event); + break; + case XCB_CONFIGURE_NOTIFY: + onConfigureNotify(event); break; - } default: break; } - ::free(event); count++; } - if(count) + if (count) xcb_flush(m_xcbConnection); } +void VisibilityMonitor::onVisibilityNotify(xcb_generic_event_t* event) +{ + xcb_visibility_notify_event_t* visibilityEvent = reinterpret_cast(event); + uint8_t visibilityState = visibilityEvent->state; + + if ((visibilityState >= VISIBILITY_UNOBSCURED) && (visibilityState < VISIBILITY_LAST)) + { + VisibilityState eState = (VisibilityState)visibilityState; + KLOG_INFO() << "visibility state changed:" + << visibilityEvent->window + << "->" << eState; + emit visibilityStateChanged(visibilityEvent->window, eState); + } + else + { + KLOG_WARNING() << "unknow visibility state:" << visibilityEvent->window << visibilityState; + } +} + +void VisibilityMonitor::onMapNotify(xcb_generic_event_t* event) +{ + xcb_map_notify_event_t* mapEvent = reinterpret_cast(event); + + if (isInternal(mapEvent->window)) + { + return; + } + + KLOG_DEBUG() << "VisibilityMonitor: MapNotify" << mapEvent->window; + emit restackedNeedRaise(); +} + +void VisibilityMonitor::onConfigureNotify(xcb_generic_event_t* event) +{ + xcb_configure_notify_event_t* configureEvent = reinterpret_cast(event); + + if (isInternal(configureEvent->window)) + { + return; + } + + KLOG_DEBUG() << "VisibilityMonitor: ConfigureNotify" << configureEvent->window; + emit restackedNeedRaise(); +} + #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) void VisibilityMonitor::onXcbSocketNotifierActivated(QSocketDescriptor socket, QSocketNotifier::Type activationEvent) diff --git a/src/view/visibility-monitor.h b/src/view/visibility-monitor.h index e485df1a97f4e1cb2da1e5240da8c116411b82f9..445d94c56b2f8f27cd1c33a81f16061ab2b76c82 100644 --- a/src/view/visibility-monitor.h +++ b/src/view/visibility-monitor.h @@ -16,8 +16,10 @@ #include #include #include +#include struct xcb_connection_t; +struct xcb_ge_generic_event_t; class QSocketNotifier; namespace Kiran { @@ -25,10 +27,9 @@ namespace ScreenSaver { /** * NOTE: - * 请注意,VisibilityNotify事件只有在Marco关闭混合的情况下,XServer才会发送 - * 该类只是一个保护措施,Marco并不管理屏保窗口,XServer也只会保证XScreenSaver窗口置顶 - * 存在可能Marco同步窗口堆叠至XServer之中时,锚定窗口位于kiran-screensaver窗口之上 - * 导致Marco窗口堆叠Stack整体窗口位于kiran-screensaver窗口之上 + * 保证 kiran-screensaver 窗口被置顶 + * 1.在不存在混成器,XServer会发出VisibilityNotify事件通知窗口可见状态 + * 2.订阅MapNotify,ConfigureNotify,窗口堆叠有更改时,重新置顶 */ class VisibilityMonitor : public QObject { @@ -46,15 +47,23 @@ public: static VisibilityMonitor* instance(); ~VisibilityMonitor() override; - void monitorWindow(WId wid); + void monitor(WId wid); + void unmonitor(WId wid); signals: void visibilityStateChanged(WId wid,VisibilityState state); + void restackedNeedRaise(); private: explicit VisibilityMonitor(QObject* parent = nullptr); void init(); + bool isInternal(WId window); + void selectSubstructureNotify(); + void unselectSubstructureNotify(); void handleXcbEvent(); + void onVisibilityNotify(xcb_generic_event_t* event); + void onMapNotify(xcb_generic_event_t* event); + void onConfigureNotify(xcb_generic_event_t* event); private slots: #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) @@ -67,6 +76,7 @@ private: bool m_inited = false; xcb_connection_t* m_xcbConnection = nullptr; QSocketNotifier* m_xcbSocketNotifier = nullptr; + QSet m_windows; }; } // namespace ScreenSaver } // namespace Kiran \ No newline at end of file