From a381836ef7d57c594a677439a3c80e944387da3e Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Sat, 17 Dec 2022 12:52:30 +0800 Subject: [PATCH] linux: further improvements Signed-off-by: Yuhang Zhao <2546789017@qq.com> --- .../Core/framelesshelper_linux.h | 138 ++++- include/FramelessHelper/Core/utils.h | 32 ++ src/core/framelesshelper_qt.cpp | 26 +- src/core/framelesshelpercore_global.cpp | 5 +- src/core/platformsupport_linux.cpp | 161 ++++++ src/core/utils_linux.cpp | 530 ++++++++++++++---- src/quick/framelessquickhelper.cpp | 10 +- src/widgets/framelesswidgetshelper.cpp | 32 +- 8 files changed, 775 insertions(+), 159 deletions(-) diff --git a/include/FramelessHelper/Core/framelesshelper_linux.h b/include/FramelessHelper/Core/framelesshelper_linux.h index 7592f79..d35ef91 100644 --- a/include/FramelessHelper/Core/framelesshelper_linux.h +++ b/include/FramelessHelper/Core/framelesshelper_linux.h @@ -129,9 +129,51 @@ using xcb_client_message_event_t = struct xcb_client_message_event_t xcb_client_message_data_t data; }; +using xcb_get_property_reply_t = struct xcb_get_property_reply_t +{ + uint8_t response_type; + uint8_t format; + uint16_t sequence; + uint32_t length; + xcb_atom_t type; + uint32_t bytes_after; + uint32_t value_len; + uint8_t pad0[12]; +}; + +using xcb_get_property_cookie_t = struct xcb_get_property_cookie_t +{ + unsigned int sequence; +}; + +using xcb_list_properties_cookie_t = struct xcb_list_properties_cookie_t +{ + unsigned int sequence; +}; + +using xcb_list_properties_reply_t = struct xcb_list_properties_reply_t +{ + uint8_t response_type; + uint8_t pad0; + uint16_t sequence; + uint32_t length; + uint16_t atoms_len; + uint8_t pad1[22]; +}; + +[[maybe_unused]] inline constexpr const auto XCB_NONE = 0; +[[maybe_unused]] inline constexpr const auto XCB_WINDOW_NONE = 0; +[[maybe_unused]] inline constexpr const auto XCB_CURRENT_TIME = 0; +[[maybe_unused]] inline constexpr const auto XCB_PROP_MODE_REPLACE = 0; +[[maybe_unused]] inline constexpr const auto XCB_ATOM_ATOM = 4; +[[maybe_unused]] inline constexpr const auto XCB_ATOM_CARDINAL = 6; +[[maybe_unused]] inline constexpr const auto XCB_ATOM_WINDOW = 33; +[[maybe_unused]] inline constexpr const auto XCB_BUTTON_INDEX_ANY = 0; +[[maybe_unused]] inline constexpr const auto XCB_BUTTON_INDEX_1 = 1; +[[maybe_unused]] inline constexpr const auto XCB_BUTTON_INDEX_2 = 2; +[[maybe_unused]] inline constexpr const auto XCB_BUTTON_INDEX_3 = 3; [[maybe_unused]] inline constexpr const auto XCB_BUTTON_RELEASE = 5; [[maybe_unused]] inline constexpr const auto XCB_CLIENT_MESSAGE = 33; -[[maybe_unused]] inline constexpr const auto XCB_BUTTON_INDEX_1 = 1; [[maybe_unused]] inline constexpr const auto XCB_EVENT_MASK_STRUCTURE_NOTIFY = 131072; [[maybe_unused]] inline constexpr const auto XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT = 1048576; [[maybe_unused]] inline constexpr const auto XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY = 524288; @@ -149,7 +191,18 @@ using xcb_client_message_event_t = struct xcb_client_message_event_t [[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_MOVE_KEYBOARD = 10; [[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_CANCEL = 11; -[[maybe_unused]] inline constexpr const char _NET_WM_MOVERESIZE_ATOM_NAME[] = "_NET_WM_MOVERESIZE"; +[[maybe_unused]] inline constexpr const char ATOM_NET_SUPPORTED[] = "_NET_SUPPORTED"; +[[maybe_unused]] inline constexpr const char ATOM_NET_WM_NAME[] = "_NET_WM_NAME"; +[[maybe_unused]] inline constexpr const char ATOM_NET_WM_MOVERESIZE[] = "_NET_WM_MOVERESIZE"; +[[maybe_unused]] inline constexpr const char ATOM_NET_SUPPORTING_WM_CHECK[] = "_NET_SUPPORTING_WM_CHECK"; +[[maybe_unused]] inline constexpr const char ATOM_NET_KDE_COMPOSITE_TOGGLING[] = "_NET_KDE_COMPOSITE_TOGGLING"; +[[maybe_unused]] inline constexpr const char ATOM_KDE_NET_WM_BLUR_BEHIND_REGION[] = "_KDE_NET_WM_BLUR_BEHIND_REGION"; +[[maybe_unused]] inline constexpr const char ATOM_GTK_SHOW_WINDOW_MENU[] = "_GTK_SHOW_WINDOW_MENU"; +[[maybe_unused]] inline constexpr const char ATOM_DEEPIN_NO_TITLEBAR[] = "_DEEPIN_NO_TITLEBAR"; +[[maybe_unused]] inline constexpr const char ATOM_DEEPIN_FORCE_DECORATE[] = "_DEEPIN_FORCE_DECORATE"; +[[maybe_unused]] inline constexpr const char ATOM_NET_WM_DEEPIN_BLUR_REGION_MASK[] = "_NET_WM_DEEPIN_BLUR_REGION_MASK"; +[[maybe_unused]] inline constexpr const char ATOM_NET_WM_DEEPIN_BLUR_REGION_ROUNDED[] = "_NET_WM_DEEPIN_BLUR_REGION_ROUNDED"; +[[maybe_unused]] inline constexpr const char ATOM_UTF8_STRING[] = "UTF8_STRING"; extern "C" { @@ -189,6 +242,87 @@ xcb_ungrab_pointer( xcb_timestamp_t time ); +FRAMELESSHELPER_CORE_API xcb_void_cookie_t +xcb_change_property( + xcb_connection_t *connection, + uint8_t mode, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint8_t format, + uint32_t data_len, + const void *data +); + +FRAMELESSHELPER_CORE_API xcb_void_cookie_t +xcb_delete_property_checked( + xcb_connection_t *connection, + xcb_window_t window, + xcb_atom_t property +); + +FRAMELESSHELPER_CORE_API xcb_get_property_cookie_t +xcb_get_property( + xcb_connection_t *connection, + uint8_t _delete, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint32_t long_offset, + uint32_t long_length +); + +FRAMELESSHELPER_CORE_API xcb_get_property_reply_t * +xcb_get_property_reply( + xcb_connection_t *connection, + xcb_get_property_cookie_t cookie, + xcb_generic_error_t **error +); + +FRAMELESSHELPER_CORE_API void * +xcb_get_property_value( + const xcb_get_property_reply_t *reply +); + +FRAMELESSHELPER_CORE_API int +xcb_get_property_value_length( + const xcb_get_property_reply_t *reply +); + +FRAMELESSHELPER_CORE_API xcb_list_properties_cookie_t +xcb_list_properties( + xcb_connection_t *connection, + xcb_window_t window +); + +FRAMELESSHELPER_CORE_API xcb_list_properties_reply_t * +xcb_list_properties_reply( + xcb_connection_t *connection, + xcb_list_properties_cookie_t cookie, + xcb_generic_error_t **error +); + +FRAMELESSHELPER_CORE_API int +xcb_list_properties_atoms_length( + const xcb_list_properties_reply_t *atom +); + +FRAMELESSHELPER_CORE_API xcb_atom_t * +xcb_list_properties_atoms( + const xcb_list_properties_reply_t *atom +); + +FRAMELESSHELPER_CORE_API xcb_get_property_cookie_t +xcb_get_property_unchecked( + xcb_connection_t *connection, + uint8_t _delete, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint32_t long_offset, + uint32_t long_length +); + } // extern "C" diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index 0cded59..c3dda87 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -25,6 +25,9 @@ #pragma once #include "framelesshelpercore_global.h" +#ifdef Q_OS_LINUX +# include "framelesshelper_linux.h" +#endif // Q_OS_LINUX FRAMELESSHELPER_BEGIN_NAMESPACE @@ -139,8 +142,37 @@ FRAMELESSHELPER_CORE_API void bringWindowToFront(const WId windowId); #endif // Q_OS_WINDOWS #ifdef Q_OS_LINUX +[[nodiscard]] FRAMELESSHELPER_CORE_API QScreen *x11_findScreenForVirtualDesktop + (const int virtualDesktopNumber); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +[[nodiscard]] FRAMELESSHELPER_CORE_API unsigned long x11_appRootWindow(const int screen); +#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 x11_appRootWindow(const int screen); +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +[[nodiscard]] FRAMELESSHELPER_CORE_API int x11_appScreen(); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 x11_appTime(); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 x11_appUserTime(); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 x11_getTimestamp(); +[[nodiscard]] FRAMELESSHELPER_CORE_API QByteArray x11_nextStartupId(); +[[nodiscard]] FRAMELESSHELPER_CORE_API Display *x11_display(); +[[nodiscard]] FRAMELESSHELPER_CORE_API xcb_connection_t *x11_connection(); +[[nodiscard]] FRAMELESSHELPER_CORE_API QByteArray getWindowProperty + (const WId windowId, const xcb_atom_t prop, const xcb_atom_t type, const quint32 data_len); +FRAMELESSHELPER_CORE_API void setWindowProperty + (const WId windowId, const xcb_atom_t prop, const xcb_atom_t type, + const void *data, const quint32 data_len = 1, const uint8_t format = 8); +FRAMELESSHELPER_CORE_API void clearWindowProperty(const WId windowId, const xcb_atom_t prop); +[[nodiscard]] FRAMELESSHELPER_CORE_API xcb_atom_t internAtom(const char *name); +[[nodiscard]] FRAMELESSHELPER_CORE_API QString getWindowManagerName(); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isSupportedByWindowManager(const xcb_atom_t atom); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isSupportedByRootWindow(const xcb_atom_t atom); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool tryHideSystemTitleBar(const WId windowId, const bool hide = true); +FRAMELESSHELPER_CORE_API void openSystemMenu(const WId windowId, const QPoint &globalPos); [[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode_linux(); [[nodiscard]] FRAMELESSHELPER_CORE_API QColor getWmThemeColor(); +FRAMELESSHELPER_CORE_API void sendMoveResizeMessage + (const WId windowId, const uint32_t action, const QPoint &globalPos, const Qt::MouseButton button = Qt::LeftButton); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isCustomDecorationSupported(); #endif // Q_OS_LINUX #ifdef Q_OS_MACOS diff --git a/src/core/framelesshelper_qt.cpp b/src/core/framelesshelper_qt.cpp index c3b57d6..a499dea 100644 --- a/src/core/framelesshelper_qt.cpp +++ b/src/core/framelesshelper_qt.cpp @@ -91,24 +91,28 @@ void FramelessHelperQt::addWindow(const SystemParameters ¶ms) const auto shouldApplyFramelessFlag = [¶ms]() -> bool { #ifdef Q_OS_MACOS const auto widget = params.getWidgetHandle(); - if (!(widget && widget->isWidgetType())) { - return false; - } -#else + return (widget && widget->isWidgetType()); +#elif defined(Q_OS_LINUX) + Q_UNUSED(params); + return !Utils::isCustomDecorationSupported(); +#else // Windows Q_UNUSED(params); -#endif return true; +#endif // Q_OS_MACOS }(); +#if (defined(Q_OS_MACOS) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))) + window->setProperty("_q_mac_wantsLayer", 1); +#endif // (defined(Q_OS_MACOS) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))) if (shouldApplyFramelessFlag) { params.setWindowFlags(params.getWindowFlags() | Qt::FramelessWindowHint); + } else { +#ifdef Q_OS_LINUX + Q_UNUSED(Utils::tryHideSystemTitleBar(windowId, true)); +#elif defined(Q_OS_MACOS) + Utils::setSystemTitleBarVisible(windowId, false); +#endif // Q_OS_LINUX } window->installEventFilter(data.eventFilter); -#ifdef Q_OS_MACOS -# if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - window->setProperty("_q_mac_wantsLayer", 1); -# endif - Utils::setSystemTitleBarVisible(windowId, false); -#endif FramelessHelper::Core::setApplicationOSThemeAware(); } diff --git a/src/core/framelesshelpercore_global.cpp b/src/core/framelesshelpercore_global.cpp index a003f2a..8b099a5 100644 --- a/src/core/framelesshelpercore_global.cpp +++ b/src/core/framelesshelpercore_global.cpp @@ -167,10 +167,6 @@ void initialize() outputLogo(); -#ifdef Q_OS_LINUX - gtk_init(nullptr, nullptr); -#endif - #ifdef Q_OS_LINUX // Qt's Wayland experience is not good, so we force the XCB backend here. // TODO: Remove this hack once Qt's Wayland implementation is good enough. @@ -178,6 +174,7 @@ void initialize() // enough, that is, before the construction of any Q(Gui)Application // instances. QCoreApplication won't instantiate the platform plugin. qputenv(QT_QPA_ENV_VAR, kxcb); + gtk_init(nullptr, nullptr); #endif #if (defined(Q_OS_MACOS) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))) diff --git a/src/core/platformsupport_linux.cpp b/src/core/platformsupport_linux.cpp index 0dc65d9..43a6aec 100644 --- a/src/core/platformsupport_linux.cpp +++ b/src/core/platformsupport_linux.cpp @@ -52,6 +52,17 @@ FRAMELESSHELPER_STRING_CONSTANT(xcb_flush) FRAMELESSHELPER_STRING_CONSTANT(xcb_intern_atom) FRAMELESSHELPER_STRING_CONSTANT(xcb_intern_atom_reply) FRAMELESSHELPER_STRING_CONSTANT(xcb_ungrab_pointer) +FRAMELESSHELPER_STRING_CONSTANT(xcb_change_property) +FRAMELESSHELPER_STRING_CONSTANT(xcb_delete_property_checked) +FRAMELESSHELPER_STRING_CONSTANT(xcb_get_property) +FRAMELESSHELPER_STRING_CONSTANT(xcb_get_property_reply) +FRAMELESSHELPER_STRING_CONSTANT(xcb_get_property_value) +FRAMELESSHELPER_STRING_CONSTANT(xcb_get_property_value_length) +FRAMELESSHELPER_STRING_CONSTANT(xcb_list_properties) +FRAMELESSHELPER_STRING_CONSTANT(xcb_list_properties_reply) +FRAMELESSHELPER_STRING_CONSTANT(xcb_list_properties_atoms_length) +FRAMELESSHELPER_STRING_CONSTANT(xcb_list_properties_atoms) +FRAMELESSHELPER_STRING_CONSTANT(xcb_get_property_unchecked) FRAMELESSHELPER_STRING_CONSTANT(gtk_init) FRAMELESSHELPER_STRING_CONSTANT(g_value_init) @@ -134,6 +145,156 @@ xcb_ungrab_pointer( return API_CALL_FUNCTION(xcb_ungrab_pointer, connection, time); } +extern "C" xcb_void_cookie_t +xcb_change_property( + xcb_connection_t *connection, + uint8_t mode, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint8_t format, + uint32_t data_len, + const void *data +) +{ + if (!API_XCB_AVAILABLE(xcb_change_property)) { + return {}; + } + return API_CALL_FUNCTION(xcb_change_property, connection, + mode, window, property, type, format, data_len, data); +} + +extern "C" xcb_void_cookie_t +xcb_delete_property_checked( + xcb_connection_t *connection, + xcb_window_t window, + xcb_atom_t property +) +{ + if (!API_XCB_AVAILABLE(xcb_delete_property_checked)) { + return {}; + } + return API_CALL_FUNCTION(xcb_delete_property_checked, connection, window, property); +} + +extern "C" xcb_get_property_cookie_t +xcb_get_property( + xcb_connection_t *connection, + uint8_t _delete, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint32_t long_offset, + uint32_t long_length +) +{ + if (!API_XCB_AVAILABLE(xcb_get_property)) { + return {}; + } + return API_CALL_FUNCTION(xcb_get_property, connection, + _delete, window, property, type, long_offset, long_length); +} + +extern "C" xcb_get_property_reply_t * +xcb_get_property_reply( + xcb_connection_t *connection, + xcb_get_property_cookie_t cookie, + xcb_generic_error_t **error +) +{ + if (!API_XCB_AVAILABLE(xcb_get_property_reply)) { + return nullptr; + } + return API_CALL_FUNCTION(xcb_get_property_reply, connection, cookie, error); +} + +extern "C" void * +xcb_get_property_value( + const xcb_get_property_reply_t *reply +) +{ + if (!API_XCB_AVAILABLE(xcb_get_property_value)) { + return nullptr; + } + return API_CALL_FUNCTION(xcb_get_property_value, reply); +} + +extern "C" int +xcb_get_property_value_length( + const xcb_get_property_reply_t *reply +) +{ + if (!API_XCB_AVAILABLE(xcb_get_property_value_length)) { + return 0; + } + return API_CALL_FUNCTION(xcb_get_property_value_length, reply); +} + +extern "C" xcb_list_properties_cookie_t +xcb_list_properties( + xcb_connection_t *connection, + xcb_window_t window +) +{ + if (!API_XCB_AVAILABLE(xcb_list_properties)) { + return {}; + } + return API_CALL_FUNCTION(xcb_list_properties, connection, window); +} + +extern "C" xcb_list_properties_reply_t * +xcb_list_properties_reply( + xcb_connection_t *connection, + xcb_list_properties_cookie_t cookie, + xcb_generic_error_t **error +) +{ + if (!API_XCB_AVAILABLE(xcb_list_properties_reply)) { + return nullptr; + } + return API_CALL_FUNCTION(xcb_list_properties_reply, connection, cookie, error); +} + +extern "C" int +xcb_list_properties_atoms_length( + const xcb_list_properties_reply_t *atom +) +{ + if (!API_XCB_AVAILABLE(xcb_list_properties_atoms_length)) { + return 0; + } + return API_CALL_FUNCTION(xcb_list_properties_atoms_length, atom); +} + +extern "C" xcb_atom_t * +xcb_list_properties_atoms( + const xcb_list_properties_reply_t *atom +) +{ + if (!API_XCB_AVAILABLE(xcb_list_properties_atoms)) { + return nullptr; + } + return API_CALL_FUNCTION(xcb_list_properties_atoms, atom); +} + +extern "C" xcb_get_property_cookie_t +xcb_get_property_unchecked( + xcb_connection_t *connection, + uint8_t _delete, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint32_t long_offset, + uint32_t long_length +) +{ + if (!API_XCB_AVAILABLE(xcb_get_property_unchecked)) { + return {}; + } + return API_CALL_FUNCTION(xcb_get_property_unchecked, connection, + _delete, window, property, type, long_offset, long_length); +} + /////////////////////////////////////////////////// // GTK diff --git a/src/core/utils_linux.cpp b/src/core/utils_linux.cpp index 0619f85..fba188e 100644 --- a/src/core/utils_linux.cpp +++ b/src/core/utils_linux.cpp @@ -73,11 +73,14 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT(startupid) FRAMELESSHELPER_BYTEARRAY_CONSTANT(display) FRAMELESSHELPER_BYTEARRAY_CONSTANT(connection) +static constexpr const auto _XCB_SEND_EVENT_MASK = + (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY); + [[maybe_unused]] [[nodiscard]] static inline int qtEdgesToWmMoveOrResizeOperation(const Qt::Edges edges) { if (edges == Qt::Edges{}) { - return -1; + return _NET_WM_MOVERESIZE_CANCEL; } if (edges & Qt::TopEdge) { if (edges & Qt::LeftEdge) { @@ -103,11 +106,10 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT(connection) if (edges & Qt::RightEdge) { return _NET_WM_MOVERESIZE_SIZE_RIGHT; } - return -1; + return _NET_WM_MOVERESIZE_CANCEL; } -[[maybe_unused]] [[nodiscard]] static inline - QScreen *x11_findScreenForVirtualDesktop(const int virtualDesktopNumber) +QScreen *Utils::x11_findScreenForVirtualDesktop(const int virtualDesktopNumber) { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE Q_UNUSED(virtualDesktopNumber); @@ -137,11 +139,9 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT(connection) } #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) -[[maybe_unused]] [[nodiscard]] static inline - unsigned long x11_appRootWindow(const int screen) +unsigned long Utils::x11_appRootWindow(const int screen) #else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) -[[maybe_unused]] [[nodiscard]] static inline - quint32 x11_appRootWindow(const int screen) +quint32 Utils::x11_appRootWindow(const int screen) #endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE @@ -163,7 +163,7 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT(connection) #endif // FRAMELESSHELPER_CORE_NO_PRIVATE } -[[maybe_unused]] [[nodiscard]] static inline int x11_appScreen() +int Utils::x11_appScreen() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE return 0; @@ -179,7 +179,7 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT(connection) #endif // FRAMELESSHELPER_CORE_NO_PRIVATE } -[[maybe_unused]] [[nodiscard]] static inline quint32 x11_appTime() +quint32 Utils::x11_appTime() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE return 0; @@ -199,7 +199,7 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT(connection) #endif // FRAMELESSHELPER_CORE_NO_PRIVATE } -[[maybe_unused]] [[nodiscard]] static inline quint32 x11_appUserTime() +quint32 Utils::x11_appUserTime() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE return 0; @@ -219,7 +219,7 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT(connection) #endif // FRAMELESSHELPER_CORE_NO_PRIVATE } -[[maybe_unused]] [[nodiscard]] static inline quint32 x11_getTimestamp() +quint32 Utils::x11_getTimestamp() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE return 0; @@ -239,7 +239,7 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT(connection) #endif // FRAMELESSHELPER_CORE_NO_PRIVATE } -[[maybe_unused]] [[nodiscard]] static inline QByteArray x11_nextStartupId() +QByteArray Utils::x11_nextStartupId() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE return {}; @@ -255,7 +255,7 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT(connection) #endif // FRAMELESSHELPER_CORE_NO_PRIVATE } -[[maybe_unused]] [[nodiscard]] static inline Display *x11_display() +Display *Utils::x11_display() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE return nullptr; @@ -280,7 +280,7 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT(connection) #endif // FRAMELESSHELPER_CORE_NO_PRIVATE } -[[maybe_unused]] [[nodiscard]] static inline xcb_connection_t *x11_connection() +xcb_connection_t *Utils::x11_connection() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE return nullptr; @@ -305,87 +305,6 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT(connection) #endif // FRAMELESSHELPER_CORE_NO_PRIVATE } -static inline void - emulateMouseButtonRelease(const WId windowId, const QPoint &globalPos, const QPoint &localPos) -{ - Q_ASSERT(windowId); - if (!windowId) { - return; - } - xcb_connection_t * const connection = x11_connection(); - Q_ASSERT(connection); - const quint32 rootWindow = x11_appRootWindow(x11_appScreen()); - Q_ASSERT(rootWindow); - xcb_button_release_event_t xev; - memset(&xev, 0, sizeof(xev)); - xev.response_type = XCB_BUTTON_RELEASE; - xev.time = x11_appTime(); - xev.root = rootWindow; - xev.root_x = globalPos.x(); - xev.root_y = globalPos.y(); - xev.event = windowId; - xev.event_x = localPos.x(); - xev.event_y = localPos.y(); - xev.same_screen = true; - xcb_send_event(connection, false, rootWindow, XCB_EVENT_MASK_STRUCTURE_NOTIFY, - reinterpret_cast(&xev)); - xcb_flush(connection); -} - -[[maybe_unused]] static inline void - doStartSystemMoveResize(const WId windowId, const QPoint &globalPos, const int edges) -{ - Q_ASSERT(windowId); - Q_ASSERT(edges >= 0); - if (!windowId || (edges < 0)) { - return; - } - xcb_connection_t * const connection = x11_connection(); - Q_ASSERT(connection); - static const auto netMoveResize = [connection]() -> xcb_atom_t { - const xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, false, - qstrlen(_NET_WM_MOVERESIZE_ATOM_NAME), _NET_WM_MOVERESIZE_ATOM_NAME); - xcb_intern_atom_reply_t * const reply = xcb_intern_atom_reply(connection, cookie, nullptr); - Q_ASSERT(reply); - const xcb_atom_t atom = reply->atom; - Q_ASSERT(atom); - std::free(reply); - return atom; - }(); - const quint32 rootWindow = x11_appRootWindow(x11_appScreen()); - Q_ASSERT(rootWindow); - xcb_client_message_event_t xev; - memset(&xev, 0, sizeof(xev)); - xev.response_type = XCB_CLIENT_MESSAGE; - xev.type = netMoveResize; - xev.window = windowId; - xev.format = 32; - xev.data.data32[0] = globalPos.x(); - xev.data.data32[1] = globalPos.y(); - xev.data.data32[2] = edges; - xev.data.data32[3] = XCB_BUTTON_INDEX_1; - // First we need to ungrab the pointer that may have been - // automatically grabbed by Qt on ButtonPressEvent. - xcb_ungrab_pointer(connection, x11_appTime()); - xcb_send_event(connection, false, rootWindow, - (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY), - reinterpret_cast(&xev)); - xcb_flush(connection); -} - -[[maybe_unused]] static inline void - sendMouseReleaseEvent(QWindow *window, const QPoint &globalPos) -{ - Q_ASSERT(window); - if (!window) { - return; - } - const QPoint nativeGlobalPos = Utils::toNativePixels(window, globalPos); - const QPoint logicalLocalPos = window->mapFromGlobal(globalPos); - const QPoint nativeLocalPos = Utils::toNativePixels(window, logicalLocalPos); - emulateMouseButtonRelease(window->winId(), nativeGlobalPos, nativeLocalPos); -} - SystemTheme Utils::getSystemTheme() { // ### TODO: how to detect high contrast mode on Linux? @@ -398,18 +317,13 @@ void Utils::startSystemMove(QWindow *window, const QPoint &globalPos) if (!window) { return; } -#if (QT_VERSION < QT_VERSION_CHECK(6, 2, 0)) - // Before we start the dragging we need to tell Qt that the mouse is released. - sendMouseReleaseEvent(window, globalPos); -#else - Q_UNUSED(globalPos); -#endif #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + Q_UNUSED(globalPos); window->startSystemMove(); -#else +#else // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) const QPoint nativeGlobalPos = Utils::toNativePixels(window, globalPos); - doStartSystemMoveResize(window->winId(), nativeGlobalPos, _NET_WM_MOVERESIZE_MOVE); -#endif + sendMoveResizeMessage(window->winId(), _NET_WM_MOVERESIZE_MOVE, nativeGlobalPos); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) } void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoint &globalPos) @@ -421,22 +335,14 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoi if (edges == Qt::Edges{}) { return; } -#if (QT_VERSION < QT_VERSION_CHECK(6, 2, 0)) - // Before we start the resizing we need to tell Qt that the mouse is released. - sendMouseReleaseEvent(window, globalPos); -#else - Q_UNUSED(globalPos); -#endif #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + Q_UNUSED(globalPos); window->startSystemResize(edges); -#else - const int section = qtEdgesToWmMoveOrResizeOperation(edges); - if (section < 0) { - return; - } +#else // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) const QPoint nativeGlobalPos = Utils::toNativePixels(window, globalPos); - doStartSystemMoveResize(window->winId(), nativeGlobalPos, section); -#endif + const int netWmOperation = qtEdgesToWmMoveOrResizeOperation(edges); + sendMoveResizeMessage(window->winId(), netWmOperation, nativeGlobalPos); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) } bool Utils::isTitleBarColorized() @@ -496,10 +402,34 @@ bool Utils::shouldAppsUseDarkMode_linux() bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, const QColor &color) { - Q_UNUSED(windowId); - Q_UNUSED(mode); Q_UNUSED(color); - return false; + Q_ASSERT(windowId); + if (!windowId) { + return false; + } + static const xcb_atom_t atom = internAtom(ATOM_KDE_NET_WM_BLUR_BEHIND_REGION); + if ((atom == XCB_NONE) || !isSupportedByRootWindow(atom)) { + WARNING << "Current window manager doesn't support blur behind window."; + return false; + } + static const xcb_atom_t deepinAtom = internAtom(ATOM_NET_WM_DEEPIN_BLUR_REGION_MASK); + if (deepinAtom != XCB_NONE) { + clearWindowProperty(windowId, deepinAtom); + } + const auto blurMode = [mode]() -> BlurMode { + if ((mode == BlurMode::Disable) || (mode == BlurMode::Default)) { + return mode; + } + WARNING << "The BlurMode::Windows_* enum values are not supported on Linux."; + return BlurMode::Default; + }(); + if (blurMode == BlurMode::Disable) { + clearWindowProperty(windowId, atom); + } else { + const quint32 value = true; + setWindowProperty(windowId, atom, XCB_ATOM_CARDINAL, &value); + } + return true; } QString Utils::getWallpaperFilePath() @@ -520,7 +450,18 @@ bool Utils::isBlurBehindWindowSupported() if (FramelessConfig::instance()->isSet(Option::ForceNonNativeBackgroundBlur)) { return false; } - // Currently not supported due to the desktop environments vary too much. + return false; // FIXME: check what's wrong. + static const QString windowManager = getWindowManagerName(); + static const bool isDeepinV15 = (windowManager == FRAMELESSHELPER_STRING_LITERAL("Mutter(DeepinGala)")); + if (isDeepinV15) { + static const xcb_atom_t atom = internAtom(ATOM_NET_WM_DEEPIN_BLUR_REGION_ROUNDED); + return ((atom != XCB_NONE) && isSupportedByWindowManager(atom)); + } + static const bool isKWin = (windowManager == FRAMELESSHELPER_STRING_LITERAL("KWin")); + if (isKWin) { + static const xcb_atom_t atom = internAtom(ATOM_KDE_NET_WM_BLUR_BEHIND_REGION); + return ((atom != XCB_NONE) && isSupportedByRootWindow(atom)); + } return false; }(); return result; @@ -552,4 +493,349 @@ QColor Utils::getFrameBorderColor(const bool active) return (active ? getWmThemeColor() : kDefaultDarkGrayColor); } +xcb_atom_t Utils::internAtom(const char *name) +{ + Q_ASSERT(name); + Q_ASSERT(*name != '\0'); + if (!name || (*name == '\0')) { + return XCB_NONE; + } + xcb_connection_t * const connection = x11_connection(); + Q_ASSERT(connection); + if (!connection) { + return XCB_NONE; + } + const xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, false, qstrlen(name), name); + xcb_intern_atom_reply_t * const reply = xcb_intern_atom_reply(connection, cookie, nullptr); + if (!reply) { + return XCB_NONE; + } + const xcb_atom_t atom = reply->atom; + std::free(reply); + return atom; +} + +QString Utils::getWindowManagerName() +{ + static const auto result = []() -> QString { + xcb_connection_t * const connection = x11_connection(); + Q_ASSERT(connection); + if (!connection) { + return {}; + } + const quint32 rootWindow = x11_appRootWindow(x11_appScreen()); + Q_ASSERT(rootWindow); + if (!rootWindow) { + return {}; + } + static const xcb_atom_t wmCheckAtom = internAtom(ATOM_NET_SUPPORTING_WM_CHECK); + if (wmCheckAtom == XCB_NONE) { + WARNING << "Failed to retrieve the atom of _NET_SUPPORTING_WM_CHECK."; + return {}; + } + const xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection, false, rootWindow, wmCheckAtom, XCB_ATOM_WINDOW, 0, 1024); + xcb_get_property_reply_t * const reply = xcb_get_property_reply(connection, cookie, nullptr); + if (!reply) { + return {}; + } + if (!((reply->format == 32) && (reply->type == XCB_ATOM_WINDOW))) { + std::free(reply); + return {}; + } + const auto windowManager = *static_cast(xcb_get_property_value(reply)); + if (windowManager == XCB_WINDOW_NONE) { + std::free(reply); + return {}; + } + static const xcb_atom_t wmNameAtom = internAtom(ATOM_NET_WM_NAME); + if (wmNameAtom == XCB_NONE) { + WARNING << "Failed to retrieve the atom of _NET_WM_NAME."; + return {}; + } + static const xcb_atom_t strAtom = internAtom(ATOM_UTF8_STRING); + if (strAtom == XCB_NONE) { + WARNING << "Failed to retrieve the atom of UTF8_STRING."; + return {}; + } + const xcb_get_property_cookie_t wmCookie = xcb_get_property_unchecked(connection, false, windowManager, wmNameAtom, strAtom, 0, 1024); + xcb_get_property_reply_t * const wmReply = xcb_get_property_reply(connection, wmCookie, nullptr); + if (!wmReply) { + std::free(reply); + return {}; + } + if (!((wmReply->format == 8) && (wmReply->type == strAtom))) { + std::free(wmReply); + std::free(reply); + return {}; + } + const auto data = static_cast(xcb_get_property_value(wmReply)); + const int len = xcb_get_property_value_length(wmReply); + const QString wmName = QString::fromUtf8(data, len); + std::free(wmReply); + std::free(reply); + return wmName; + }(); + return result; +} + +void Utils::openSystemMenu(const WId windowId, const QPoint &globalPos) +{ + Q_ASSERT(windowId); + if (!windowId) { + return; + } + + xcb_connection_t * const connection = x11_connection(); + Q_ASSERT(connection); + if (!connection) { + return; + } + + const quint32 rootWindow = x11_appRootWindow(x11_appScreen()); + Q_ASSERT(rootWindow); + if (!rootWindow) { + return; + } + + static const xcb_atom_t atom = internAtom(ATOM_GTK_SHOW_WINDOW_MENU); + if ((atom == XCB_NONE) || !isSupportedByWindowManager(atom)) { + WARNING << "Current window manager doesn't support showing window menu."; + return; + } + + xcb_client_message_event_t xev; + memset(&xev, 0, sizeof(xev)); + xev.response_type = XCB_CLIENT_MESSAGE; + xev.type = atom; + xev.window = windowId; + xev.format = 32; + xev.data.data32[1] = globalPos.x(); + xev.data.data32[2] = globalPos.y(); + + xcb_ungrab_pointer(connection, XCB_CURRENT_TIME); + xcb_send_event(connection, false, rootWindow, _XCB_SEND_EVENT_MASK, reinterpret_cast(&xev)); + xcb_flush(connection); +} + +QByteArray Utils::getWindowProperty(const WId windowId, const xcb_atom_t prop, const xcb_atom_t type, const quint32 data_len) +{ + Q_ASSERT(windowId); + Q_ASSERT(prop != XCB_NONE); + Q_ASSERT(type != XCB_NONE); + if (!windowId || (prop == XCB_NONE) || (type == XCB_NONE)) { + return {}; + } + xcb_connection_t * const connection = x11_connection(); + Q_ASSERT(connection); + if (!connection) { + return {}; + } + const xcb_get_property_cookie_t cookie = xcb_get_property(connection, false, windowId, prop, type, 0, data_len); + xcb_get_property_reply_t * const reply = xcb_get_property_reply(connection, cookie, nullptr); + if (!reply) { + return {}; + } + QByteArray data = {}; + const int len = xcb_get_property_value_length(reply); + const auto buf = static_cast(xcb_get_property_value(reply)); + data.append(buf, len); + std::free(reply); + return data; +} + +void Utils::setWindowProperty(const WId windowId, const xcb_atom_t prop, const xcb_atom_t type, const void *data, const quint32 data_len, const uint8_t format) +{ + Q_ASSERT(windowId); + Q_ASSERT(prop != XCB_NONE); + Q_ASSERT(type != XCB_NONE); + if (!windowId || (prop == XCB_NONE) || (type == XCB_NONE)) { + return; + } + xcb_connection_t * const connection = x11_connection(); + Q_ASSERT(connection); + if (!connection) { + return; + } + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, windowId, prop, type, format, data_len, data); + xcb_flush(connection); +} + +void Utils::clearWindowProperty(const WId windowId, const xcb_atom_t prop) +{ + Q_ASSERT(windowId); + Q_ASSERT(prop != XCB_NONE); + if (!windowId || (prop == XCB_NONE)) { + return; + } + xcb_connection_t * const connection = x11_connection(); + Q_ASSERT(connection); + if (!connection) { + return; + } + xcb_delete_property_checked(connection, windowId, prop); +} + +bool Utils::isSupportedByWindowManager(const xcb_atom_t atom) +{ + Q_ASSERT(atom != XCB_NONE); + if (atom == XCB_NONE) { + return false; + } + static const auto netWmAtoms = []() -> QList { + xcb_connection_t * const connection = x11_connection(); + Q_ASSERT(connection); + if (!connection) { + return {}; + } + const quint32 rootWindow = x11_appRootWindow(x11_appScreen()); + Q_ASSERT(rootWindow); + if (!rootWindow) { + return {}; + } + static const xcb_atom_t netSupportedAtom = internAtom(ATOM_NET_SUPPORTED); + if (netSupportedAtom == XCB_NONE) { + WARNING << "Failed to retrieve the atom of _NET_SUPPORTED."; + return {}; + } + QList result = {}; + int offset = 0; + int remaining = 0; + do { + const xcb_get_property_cookie_t cookie = xcb_get_property(connection, false, rootWindow, netSupportedAtom, XCB_ATOM_ATOM, offset, 1024); + xcb_get_property_reply_t * const reply = xcb_get_property_reply(connection, cookie, nullptr); + if (!reply) { + break; + } + remaining = 0; + if ((reply->type == XCB_ATOM_ATOM) && (reply->format == 32)) { + const int len = (xcb_get_property_value_length(reply) / sizeof(xcb_atom_t)); + const auto atoms = static_cast(xcb_get_property_value(reply)); + const int size = result.size(); + result.resize(size + len); + std::memcpy(result.data() + size, atoms, len * sizeof(xcb_atom_t)); + remaining = reply->bytes_after; + offset += len; + } + std::free(reply); + } while (remaining > 0); + return result; + }(); + return netWmAtoms.contains(atom); +} + +bool Utils::isSupportedByRootWindow(const xcb_atom_t atom) +{ + Q_ASSERT(atom != XCB_NONE); + if (atom == XCB_NONE) { + return false; + } + static const auto rootWindowProperties = []() -> QList { + xcb_connection_t * const connection = x11_connection(); + Q_ASSERT(connection); + if (!connection) { + return {}; + } + const quint32 rootWindow = x11_appRootWindow(x11_appScreen()); + Q_ASSERT(rootWindow); + if (!rootWindow) { + return {}; + } + QList result = {}; + const xcb_list_properties_cookie_t cookie = xcb_list_properties(connection, rootWindow); + xcb_list_properties_reply_t * const reply = xcb_list_properties_reply(connection, cookie, nullptr); + if (!reply) { + return {}; + } + const int len = xcb_list_properties_atoms_length(reply); + const auto atoms = static_cast(xcb_list_properties_atoms(reply)); + result.resize(len); + std::memcpy(result.data(), atoms, len * sizeof(xcb_atom_t)); + std::free(reply); + return result; + }(); + return rootWindowProperties.contains(atom); +} + +bool Utils::tryHideSystemTitleBar(const WId windowId, const bool hide) +{ + Q_ASSERT(windowId); + if (!windowId) { + return false; + } + static const xcb_atom_t deepinNoTitleBarAtom = internAtom(ATOM_DEEPIN_NO_TITLEBAR); + if ((deepinNoTitleBarAtom == XCB_NONE) || !isSupportedByWindowManager(deepinNoTitleBarAtom)) { + WARNING << "Current window manager doesn't support hiding title bar natively."; + return false; + } + const quint32 value = hide; + setWindowProperty(windowId, deepinNoTitleBarAtom, XCB_ATOM_CARDINAL, &value); + static const xcb_atom_t deepinForceDecorateAtom = internAtom(ATOM_DEEPIN_FORCE_DECORATE); + if ((deepinForceDecorateAtom == XCB_NONE) || !isSupportedByWindowManager(deepinForceDecorateAtom)) { + return true; + } + if (hide) { + setWindowProperty(windowId, deepinForceDecorateAtom, XCB_ATOM_CARDINAL, &value); + } else { + clearWindowProperty(windowId, deepinForceDecorateAtom); + } + return true; +} + +void Utils::sendMoveResizeMessage(const WId windowId, const uint32_t action, const QPoint &globalPos, const Qt::MouseButton button) +{ + Q_ASSERT(windowId); + if (!windowId) { + return; + } + + xcb_connection_t * const connection = x11_connection(); + Q_ASSERT(connection); + if (!connection) { + return; + } + const quint32 rootWindow = x11_appRootWindow(x11_appScreen()); + Q_ASSERT(rootWindow); + if (!rootWindow) { + return; + } + + static const xcb_atom_t atom = internAtom(ATOM_NET_WM_MOVERESIZE); + if ((atom == XCB_NONE) || !isSupportedByWindowManager(atom)) { + WARNING << "Current window manager doesn't support move resize operation."; + return; + } + + xcb_client_message_event_t xev; + memset(&xev, 0, sizeof(xev)); + xev.response_type = XCB_CLIENT_MESSAGE; + xev.type = atom; + xev.window = windowId; + xev.format = 32; + xev.data.data32[0] = globalPos.x(); + xev.data.data32[1] = globalPos.y(); + xev.data.data32[2] = action; + xev.data.data32[3] = [button]() -> int { + if (button == Qt::LeftButton) { + return XCB_BUTTON_INDEX_1; + } + if (button == Qt::RightButton) { + return XCB_BUTTON_INDEX_3; + } + return XCB_BUTTON_INDEX_ANY; + }(); + xev.data.data32[4] = 0; + + if (action != _NET_WM_MOVERESIZE_CANCEL) { + xcb_ungrab_pointer(connection, XCB_CURRENT_TIME); + } + xcb_send_event(connection, false, rootWindow, _XCB_SEND_EVENT_MASK, reinterpret_cast(&xev)); + xcb_flush(connection); +} + +bool Utils::isCustomDecorationSupported() +{ + static const xcb_atom_t atom = internAtom(ATOM_DEEPIN_NO_TITLEBAR); + return ((atom != XCB_NONE) && isSupportedByWindowManager(atom)); +} + FRAMELESSHELPER_END_NAMESPACE diff --git a/src/quick/framelessquickhelper.cpp b/src/quick/framelessquickhelper.cpp index 4bd69f7..c999d6d 100644 --- a/src/quick/framelessquickhelper.cpp +++ b/src/quick/framelessquickhelper.cpp @@ -352,17 +352,21 @@ void FramelessQuickHelperPrivate::setHitTestVisible(QObject *object, const bool void FramelessQuickHelperPrivate::showSystemMenu(const QPoint &pos) { -#ifdef Q_OS_WINDOWS Q_Q(FramelessQuickHelper); const QQuickWindow * const window = q->window(); if (!window) { return; } + const WId windowId = window->winId(); const QPoint globalPos = window->mapToGlobal(pos); const QPoint nativePos = Utils::toNativePixels(window, globalPos); - Utils::showSystemMenu(window->winId(), nativePos, false, [this]() -> bool { return isWindowFixedSize(); }); +#ifdef Q_OS_WINDOWS + Utils::showSystemMenu(windowId, nativePos, false, [this]() -> bool { return isWindowFixedSize(); }); +#elif defined(Q_OS_LINUX) + Utils::openSystemMenu(windowId, nativePos); #else - Q_UNUSED(pos); + Q_UNUSED(windowId); + Q_UNUSED(nativePos); #endif } diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index 3dbf80d..9cef0dd 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -189,23 +189,18 @@ void FramelessWidgetsHelperPrivate::setBlurBehindWindowEnabled(const bool enable return; } if (Utils::isBlurBehindWindowSupported()) { - BlurMode mode = BlurMode::Disable; +#ifdef Q_OS_WINDOWS QPalette palette = m_window->palette(); if (enable) { - if (!m_savedWindowBackgroundColor.isValid()) { - m_savedWindowBackgroundColor = palette.color(QPalette::Window); - } - palette.setColor(QPalette::Window, kDefaultTransparentColor); - mode = BlurMode::Default; - } else { - if (m_savedWindowBackgroundColor.isValid()) { - palette.setColor(QPalette::Window, m_savedWindowBackgroundColor); - m_savedWindowBackgroundColor = {}; - } - mode = BlurMode::Disable; + m_savedWindowBackgroundColor = palette.color(QPalette::Window); } + palette.setColor(QPalette::Window, (enable ? kDefaultTransparentColor : m_savedWindowBackgroundColor)); m_window->setPalette(palette); - if (Utils::setBlurBehindWindowEnabled(m_window->winId(), mode, color)) { +#else // !Q_OS_WINDOWS + m_window->setAttribute(Qt::WA_TranslucentBackground, enable); +#endif // Q_OS_WINDOWS + if (Utils::setBlurBehindWindowEnabled(m_window->winId(), + (enable ? BlurMode::Default : BlurMode::Disable), color)) { m_blurBehindWindowEnabled = enable; emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("blurBehindWindowEnabledChanged")); } else { @@ -804,16 +799,19 @@ void FramelessWidgetsHelperPrivate::bringWindowToFront() void FramelessWidgetsHelperPrivate::showSystemMenu(const QPoint &pos) { -#ifdef Q_OS_WINDOWS if (!m_window) { return; } + const WId windowId = m_window->winId(); const QPoint globalPos = m_window->mapToGlobal(pos); const QPoint nativePos = Utils::toNativePixels(m_window->windowHandle(), globalPos); - Utils::showSystemMenu(m_window->winId(), nativePos, false, [this]() -> bool { return isWindowFixedSize(); }); +#ifdef Q_OS_WINDOWS + Utils::showSystemMenu(windowId, nativePos, false, [this]() -> bool { return isWindowFixedSize(); }); +#elif defined(Q_OS_LINUX) + Utils::openSystemMenu(windowId, nativePos); #else - // ### TODO - Q_UNUSED(pos); + Q_UNUSED(windowId); + Q_UNUSED(nativePos); #endif }