linux: further improvements

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-12-17 12:52:30 +08:00
parent 840cf3060d
commit a381836ef7
8 changed files with 775 additions and 159 deletions

View File

@ -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"

View File

@ -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

View File

@ -91,24 +91,28 @@ void FramelessHelperQt::addWindow(const SystemParameters &params)
const auto shouldApplyFramelessFlag = [&params]() -> 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();
}

View File

@ -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)))

View File

@ -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

View File

@ -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<const char *>(&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<const char *>(&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_window_t *>(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<const char *>(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<const char *>(&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<const char *>(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_atom_t> {
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<xcb_atom_t> 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_atom_t *>(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_atom_t> {
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<xcb_atom_t> 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_atom_t *>(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<const char *>(&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

View File

@ -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
}

View File

@ -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
}