diff --git a/CMakeLists.txt b/CMakeLists.txt index 67e179d..de68794 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,10 +45,6 @@ if(FRAMELESSHELPER_NO_BUNDLE_RESOURCE) message(WARNING "Nothing will be embeded into FramelessHelper, the chrome buttons will have no icon.") endif() -if(FRAMELESSHELPER_NO_PRIVATE AND UNIX AND NOT APPLE) - message(SEND_ERROR "Disable private Qt functionalities will completely break FramelessHelper's Linux experience for now.") -endif() - if(FRAMELESSHELPER_ENABLE_VCLTL AND NOT MSVC) message(WARNING "VC-LTL is only available for the MSVC toolchain.") endif() diff --git a/include/FramelessHelper/Core/framelesshelper_linux.h b/include/FramelessHelper/Core/framelesshelper_linux.h index d35ef91..68ed954 100644 --- a/include/FramelessHelper/Core/framelesshelper_linux.h +++ b/include/FramelessHelper/Core/framelesshelper_linux.h @@ -54,8 +54,11 @@ */ using Display = struct _XDisplay; -using xcb_connection_t = struct xcb_connection_t; +#if __has_include() +# include +#else // !__has_include() +using xcb_connection_t = struct xcb_connection_t; using xcb_button_t = uint8_t; using xcb_window_t = uint32_t; using xcb_timestamp_t = uint32_t; @@ -177,6 +180,7 @@ using xcb_list_properties_reply_t = struct xcb_list_properties_reply_t [[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; +#endif // __has_include() [[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0; [[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOP = 1; @@ -350,6 +354,9 @@ xcb_get_property_unchecked( * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +#if __has_include() +# include +#else // !__has_include() #define G_VALUE_INIT { 0, { { 0 } } } #define g_signal_connect(instance, detailed_signal, c_handler, data) \ g_signal_connect_data((instance), (detailed_signal), (c_handler), (data), nullptr, G_CONNECT_DEFAULT) @@ -367,8 +374,8 @@ using gchar = char; using guchar = unsigned char; using gchararray = char *; using gpointer = void *; -using gint64 = int64_t; -using guint64 = uint64_t; +using gint64 = signed long; +using guint64 = unsigned long; using gsize = unsigned int; using GType = unsigned long; @@ -404,11 +411,13 @@ struct _GValue gpointer v_pointer; } data[2]; }; +#endif // __has_include() [[maybe_unused]] inline constexpr const char GTK_THEME_NAME_ENV_VAR[] = "GTK_THEME"; [[maybe_unused]] inline constexpr const char GTK_THEME_NAME_PROP[] = "gtk-theme-name"; [[maybe_unused]] inline constexpr const char GTK_THEME_PREFER_DARK_PROP[] = "gtk-application-prefer-dark-theme"; +#if 0 extern "C" { @@ -482,3 +491,7 @@ g_clear_object( ); } // extern "C" +#endif + +template +T gtkSettings(const gchar *property); diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index c3dda87..77b222b 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -160,7 +160,7 @@ FRAMELESSHELPER_CORE_API void bringWindowToFront(const WId windowId); (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); + const void *data, const quint32 data_len, const uint8_t format); 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(); @@ -173,6 +173,8 @@ FRAMELESSHELPER_CORE_API void openSystemMenu(const WId windowId, const QPoint &g 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(); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool + setPlatformPropertiesForWindow(QWindow *window, const QVariantHash &props); #endif // Q_OS_LINUX #ifdef Q_OS_MACOS diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 938a82a..6144b19 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -22,6 +22,14 @@ SOFTWARE. ]] +if(UNIX AND NOT APPLE) + if(FRAMELESSHELPER_NO_PRIVATE) + find_package(Qt5 QUIET COMPONENTS X11Extras) + endif() + find_package(PkgConfig REQUIRED) + pkg_check_modules(GTK3 REQUIRED gtk+-3.0) +endif() + set(SUB_MOD_NAME Core) set(SUB_PROJ_NAME ${PROJECT_NAME}${SUB_MOD_NAME}) set(SUB_PROJ_PATH ${PROJECT_NAME}/${SUB_MOD_NAME}) @@ -178,6 +186,13 @@ if(APPLE) "-framework Cocoa" "-framework AppKit" ) +elseif(UNIX) + target_include_directories(${SUB_PROJ_NAME} PRIVATE + ${GTK3_INCLUDE_DIRS} + ) + target_link_libraries(${SUB_PROJ_NAME} PRIVATE + ${GTK3_LINK_LIBRARIES} + ) endif() if(FRAMELESSHELPER_NO_PRIVATE) @@ -185,6 +200,13 @@ if(FRAMELESSHELPER_NO_PRIVATE) Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui ) + # Qt X11Extras was first introduced in 5.1 and got removed in 6.0 + # But it was again brought back as a private feature of QtGui in 6.2 + if(TARGET Qt5::X11Extras) + target_link_libraries(${SUB_PROJ_NAME} PRIVATE + Qt5::X11Extras + ) + endif() else() target_link_libraries(${SUB_PROJ_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::CorePrivate diff --git a/src/core/platformsupport_linux.cpp b/src/core/platformsupport_linux.cpp index 43a6aec..b02562a 100644 --- a/src/core/platformsupport_linux.cpp +++ b/src/core/platformsupport_linux.cpp @@ -25,27 +25,7 @@ #include "sysapiloader_p.h" #include "framelesshelper_linux.h" -#define GTK_SETTINGS(Name, Type, ...) \ - Type Name(const gchar *property) \ - { \ - Q_ASSERT(property); \ - if (!property) { \ - return {}; \ - } \ - GtkSettings *settings = gtk_settings_get_default(); \ - Q_ASSERT(settings); \ - if (!settings) { \ - return {}; \ - } \ - GValue value = G_VALUE_INIT; \ - g_object_get_property(reinterpret_cast(settings), property, &value); \ - __VA_ARGS__ \ - g_value_unset(&value); \ - return result; \ - } - FRAMELESSHELPER_STRING_CONSTANT(libxcb) -FRAMELESSHELPER_STRING_CONSTANT2(libgtk, "libgtk-3") FRAMELESSHELPER_STRING_CONSTANT(xcb_send_event) FRAMELESSHELPER_STRING_CONSTANT(xcb_flush) @@ -64,19 +44,6 @@ 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) -FRAMELESSHELPER_STRING_CONSTANT(g_value_reset) -FRAMELESSHELPER_STRING_CONSTANT(g_value_unset) -FRAMELESSHELPER_STRING_CONSTANT(g_value_get_boolean) -FRAMELESSHELPER_STRING_CONSTANT(g_value_get_string) -FRAMELESSHELPER_STRING_CONSTANT(gtk_settings_get_default) -FRAMELESSHELPER_STRING_CONSTANT(g_object_get_property) -FRAMELESSHELPER_STRING_CONSTANT(g_signal_connect_data) -FRAMELESSHELPER_STRING_CONSTANT(g_free) -FRAMELESSHELPER_STRING_CONSTANT(g_object_unref) -FRAMELESSHELPER_STRING_CONSTANT(g_clear_object) - ////////////////////////////////////////////// // XCB @@ -298,6 +265,41 @@ xcb_get_property_unchecked( /////////////////////////////////////////////////// // GTK +#if 0 +#define GTK_SETTINGS_IMPL(Name, Type, ...) \ + Type Name(const gchar *property) \ + { \ + Q_ASSERT(property); \ + if (!property) { \ + return Type{}; \ + } \ + static GtkSettings * const settings = gtk_settings_get_default(); \ + Q_ASSERT(settings); \ + if (!settings) { \ + return Type{}; \ + } \ + GValue value = G_VALUE_INIT; \ + g_object_get_property(reinterpret_cast(settings), property, &value); \ + __VA_ARGS__ \ + g_value_unset(&value); \ + return result; \ + } + +FRAMELESSHELPER_STRING_CONSTANT2(libgtk, "libgtk-3") + +FRAMELESSHELPER_STRING_CONSTANT(gtk_init) +FRAMELESSHELPER_STRING_CONSTANT(g_value_init) +FRAMELESSHELPER_STRING_CONSTANT(g_value_reset) +FRAMELESSHELPER_STRING_CONSTANT(g_value_unset) +FRAMELESSHELPER_STRING_CONSTANT(g_value_get_boolean) +FRAMELESSHELPER_STRING_CONSTANT(g_value_get_string) +FRAMELESSHELPER_STRING_CONSTANT(gtk_settings_get_default) +FRAMELESSHELPER_STRING_CONSTANT(g_object_get_property) +FRAMELESSHELPER_STRING_CONSTANT(g_signal_connect_data) +FRAMELESSHELPER_STRING_CONSTANT(g_free) +FRAMELESSHELPER_STRING_CONSTANT(g_object_unref) +FRAMELESSHELPER_STRING_CONSTANT(g_clear_object) + extern "C" void gtk_init( int *argc, @@ -439,5 +441,33 @@ g_clear_object( API_CALL_FUNCTION(g_clear_object, object_ptr); } -GTK_SETTINGS(GTK_bool, bool, const bool result = g_value_get_boolean(&value);) -GTK_SETTINGS(GTK_str, QString, const QString result = QUtf8String(g_value_get_string(&value));) +GTK_SETTINGS_IMPL(GTK_bool, bool, const bool result = g_value_get_boolean(&value);) +GTK_SETTINGS_IMPL(GTK_str, QString, const QString result = QUtf8String(g_value_get_string(&value));) +#endif + +template +T gtkSettings(const gchar *property) +{ + Q_ASSERT(property); + Q_ASSERT(*property != '\0'); + if (!property || (*property == '\0')) { + return T{}; + } + static GtkSettings * const settings = gtk_settings_get_default(); + if (!settings) { + return T{}; + } + T result = {}; + g_object_get(settings, property, &result, nullptr); + return result; +} + +template bool gtkSettings(const gchar *); + +QString gtkSettings(const gchar *property) +{ + const auto raw = gtkSettings(property); + const QString result = QUtf8String(raw); + g_free(raw); + return result; +} diff --git a/src/core/utils_linux.cpp b/src/core/utils_linux.cpp index b05cb1b..8ccd3d7 100644 --- a/src/core/utils_linux.cpp +++ b/src/core/utils_linux.cpp @@ -23,7 +23,6 @@ */ #include "utils.h" -#include "framelesshelper_linux.h" #include "framelessconfig_p.h" #include "framelessmanager.h" #include "framelessmanager_p.h" @@ -31,7 +30,12 @@ #include #include #include -#ifndef FRAMELESSHELPER_CORE_NO_PRIVATE +#ifdef FRAMELESSHELPER_CORE_NO_PRIVATE +# if __has_include() +# include +# define FRAMELESSHELPER_HAS_X11EXTRAS +# endif // __has_include() +#else // !FRAMELESSHELPER_CORE_NO_PRIVATE # include # include # if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) @@ -42,8 +46,8 @@ # endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #endif // FRAMELESSHELPER_CORE_NO_PRIVATE -extern bool GTK_bool(const gchar *); -extern QString GTK_str(const gchar *); +extern template bool gtkSettings(const gchar *); +extern QString gtkSettings(const gchar *); FRAMELESSHELPER_BEGIN_NAMESPACE @@ -146,8 +150,12 @@ quint32 Utils::x11_appRootWindow(const int screen) #endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE +# ifdef FRAMELESSHELPER_HAS_X11EXTRAS + return QX11Info::appRootWindow(screen); +# else // !FRAMELESSHELPER_HAS_X11EXTRAS Q_UNUSED(screen); return 0; +# endif // FRAMELESSHELPER_HAS_X11EXTRAS #else // !FRAMELESSHELPER_CORE_NO_PRIVATE if (!qApp) { return 0; @@ -167,7 +175,11 @@ quint32 Utils::x11_appRootWindow(const int screen) int Utils::x11_appScreen() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE +# ifdef FRAMELESSHELPER_HAS_X11EXTRAS + return QX11Info::appScreen(); +# else // !FRAMELESSHELPER_HAS_X11EXTRAS return 0; +# endif // FRAMELESSHELPER_HAS_X11EXTRAS #else // !FRAMELESSHELPER_CORE_NO_PRIVATE if (!qApp) { return 0; @@ -183,7 +195,11 @@ int Utils::x11_appScreen() quint32 Utils::x11_appTime() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE +# ifdef FRAMELESSHELPER_HAS_X11EXTRAS + return QX11Info::appTime(); +# else // !FRAMELESSHELPER_HAS_X11EXTRAS return 0; +# endif // FRAMELESSHELPER_HAS_X11EXTRAS #else // !FRAMELESSHELPER_CORE_NO_PRIVATE if (!qApp) { return 0; @@ -203,7 +219,11 @@ quint32 Utils::x11_appTime() quint32 Utils::x11_appUserTime() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE +# ifdef FRAMELESSHELPER_HAS_X11EXTRAS + return QX11Info::appUserTime(); +# else // !FRAMELESSHELPER_HAS_X11EXTRAS return 0; +# endif // FRAMELESSHELPER_HAS_X11EXTRAS #else // !FRAMELESSHELPER_CORE_NO_PRIVATE if (!qApp) { return 0; @@ -223,7 +243,11 @@ quint32 Utils::x11_appUserTime() quint32 Utils::x11_getTimestamp() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE +# ifdef FRAMELESSHELPER_HAS_X11EXTRAS + return QX11Info::getTimestamp(); +# else // !FRAMELESSHELPER_HAS_X11EXTRAS return 0; +# endif // FRAMELESSHELPER_HAS_X11EXTRAS #else // !FRAMELESSHELPER_CORE_NO_PRIVATE if (!qApp) { return 0; @@ -243,7 +267,11 @@ quint32 Utils::x11_getTimestamp() QByteArray Utils::x11_nextStartupId() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE +# ifdef FRAMELESSHELPER_HAS_X11EXTRAS + return QX11Info::nextStartupId(); +# else // !FRAMELESSHELPER_HAS_X11EXTRAS return {}; +# endif // FRAMELESSHELPER_HAS_X11EXTRAS #else // !FRAMELESSHELPER_CORE_NO_PRIVATE if (!qApp) { return {}; @@ -259,7 +287,11 @@ QByteArray Utils::x11_nextStartupId() Display *Utils::x11_display() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE +# ifdef FRAMELESSHELPER_HAS_X11EXTRAS + return QX11Info::display(); +# else // !FRAMELESSHELPER_HAS_X11EXTRAS return nullptr; +# endif // FRAMELESSHELPER_HAS_X11EXTRAS #else // !FRAMELESSHELPER_CORE_NO_PRIVATE if (!qApp) { return nullptr; @@ -284,7 +316,11 @@ Display *Utils::x11_display() xcb_connection_t *Utils::x11_connection() { #ifdef FRAMELESSHELPER_CORE_NO_PRIVATE +# ifdef FRAMELESSHELPER_HAS_X11EXTRAS + return QX11Info::connection(); +# else // !FRAMELESSHELPER_HAS_X11EXTRAS return nullptr; +# endif // FRAMELESSHELPER_HAS_X11EXTRAS #else // !FRAMELESSHELPER_CORE_NO_PRIVATE if (!qApp) { return nullptr; @@ -385,7 +421,7 @@ bool Utils::shouldAppsUseDarkMode_linux() gtk-theme-name provides both light and dark variants. We can save a regex check by testing this property first. */ - const bool preferDark = GTK_bool(GTK_THEME_PREFER_DARK_PROP); + const auto preferDark = gtkSettings(GTK_THEME_PREFER_DARK_PROP); if (preferDark) { return true; } @@ -393,7 +429,7 @@ bool Utils::shouldAppsUseDarkMode_linux() /* https://docs.gtk.org/gtk3/property.Settings.gtk-theme-name.html */ - const QString curThemeName = GTK_str(GTK_THEME_NAME_PROP); + const auto curThemeName = gtkSettings(GTK_THEME_NAME_PROP); if (!curThemeName.isEmpty()) { return curThemeName.contains(kdark, Qt::CaseInsensitive); } @@ -428,7 +464,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, clearWindowProperty(windowId, atom); } else { const quint32 value = true; - setWindowProperty(windowId, atom, XCB_ATOM_CARDINAL, &value); + setWindowProperty(windowId, atom, XCB_ATOM_CARDINAL, &value, 1, sizeof(quint32) * 8); } return true; } @@ -779,13 +815,13 @@ bool Utils::tryHideSystemTitleBar(const WId windowId, const bool hide) return false; } const quint32 value = hide; - setWindowProperty(windowId, deepinNoTitleBarAtom, XCB_ATOM_CARDINAL, &value); + setWindowProperty(windowId, deepinNoTitleBarAtom, XCB_ATOM_CARDINAL, &value, 1, sizeof(quint32) * 8); 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); + setWindowProperty(windowId, deepinForceDecorateAtom, XCB_ATOM_CARDINAL, &value, 1, sizeof(quint32) * 8); } else { clearWindowProperty(windowId, deepinForceDecorateAtom); } @@ -849,4 +885,41 @@ bool Utils::isCustomDecorationSupported() return ((atom != XCB_NONE) && isSupportedByWindowManager(atom)); } +bool Utils::setPlatformPropertiesForWindow(QWindow *window, const QVariantHash &props) +{ + Q_ASSERT(window); + Q_ASSERT(!props.isEmpty()); + if (!window || props.isEmpty()) { + return false; + } + static const auto object = [window]() -> QObject * { + if (!qGuiApp) { + return nullptr; + } + using buildNativeSettingsPtr = bool(*)(QObject *, WId); + static const auto pbuildNativeSettings + = reinterpret_cast( + QGuiApplication::platformFunction( + FRAMELESSHELPER_BYTEARRAY_LITERAL("_d_buildNativeSettings"))); + if (!pbuildNativeSettings) { + return nullptr; + } + const auto obj = new QObject(window); + if (!pbuildNativeSettings(obj, window->winId())) { + delete obj; + return nullptr; + } + return obj; + }(); + if (!object) { + return false; + } + auto it = props.constBegin(); + while (it != props.constEnd()) { + object->setProperty(qUtf8Printable(it.key()), it.value()); + ++it; + } + return true; +} + FRAMELESSHELPER_END_NAMESPACE diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index 9cef0dd..13830ce 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -189,14 +189,13 @@ void FramelessWidgetsHelperPrivate::setBlurBehindWindowEnabled(const bool enable return; } if (Utils::isBlurBehindWindowSupported()) { -#ifdef Q_OS_WINDOWS QPalette palette = m_window->palette(); if (enable) { m_savedWindowBackgroundColor = palette.color(QPalette::Window); } palette.setColor(QPalette::Window, (enable ? kDefaultTransparentColor : m_savedWindowBackgroundColor)); m_window->setPalette(palette); -#else // !Q_OS_WINDOWS +#ifndef Q_OS_WINDOWS m_window->setAttribute(Qt::WA_TranslucentBackground, enable); #endif // Q_OS_WINDOWS if (Utils::setBlurBehindWindowEnabled(m_window->winId(), @@ -296,8 +295,7 @@ WidgetsSharedHelper *FramelessWidgetsHelperPrivate::findOrCreateSharedHelper(QWi QWidget * const topLevelWindow = window->window(); WidgetsSharedHelper *helper = topLevelWindow->findChild(); if (!helper) { - helper = new WidgetsSharedHelper; - helper->setParent(topLevelWindow); + helper = new WidgetsSharedHelper(topLevelWindow); helper->setup(topLevelWindow); } return helper;