Files
RedBear-OS/local/recipes/kde/kf6-kwindowsystem/source/src/kwindowinfo.cpp
T
2026-04-14 10:51:06 +01:00

607 lines
18 KiB
C++

/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kwindowinfo.h"
#include "kwindowsystem.h"
#include "kwindowsystem_debug.h"
#include "kx11extras.h"
#include "netwm.h"
#include <config-kwindowsystem.h>
#include "private/qtx11extras_p.h"
#include <QDebug>
#include <QRect>
#include "kxerrorhandler_p.h"
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <xcb/res.h>
#include "cptr_p.h"
static bool haveXRes()
{
static bool s_checked = false;
static bool s_haveXRes = false;
if (!s_checked) {
auto cookie = xcb_res_query_version(QX11Info::connection(), XCB_RES_MAJOR_VERSION, XCB_RES_MINOR_VERSION);
UniqueCPointer<xcb_res_query_version_reply_t> reply(xcb_res_query_version_reply(QX11Info::connection(), cookie, nullptr));
s_haveXRes = reply != nullptr;
s_checked = true;
}
return s_haveXRes;
}
class Q_DECL_HIDDEN KWindowInfoPrivate : public QSharedData
{
public:
WId window;
NET::Properties properties;
NET::Properties2 properties2;
std::unique_ptr<NETWinInfo> m_info;
QString m_name;
QString m_iconic_name;
QRect m_geometry;
QRect m_frame_geometry;
int m_pid = -1; // real PID from XResources. Valid if > 0
bool m_valid = false;
};
KWindowInfo::KWindowInfo(WId window, NET::Properties properties, NET::Properties2 properties2)
: d(new KWindowInfoPrivate)
{
d->window = window;
d->properties = properties;
d->properties2 = properties2;
if (!KWindowSystem::isPlatformX11()) {
return;
}
KXErrorHandler handler;
if (properties & NET::WMVisibleIconName) {
properties |= NET::WMIconName | NET::WMVisibleName; // force, in case it will be used as a fallback
}
if (properties & NET::WMVisibleName) {
properties |= NET::WMName; // force, in case it will be used as a fallback
}
if (properties2 & NET::WM2ExtendedStrut) {
properties |= NET::WMStrut; // will be used as fallback
}
if (properties & NET::WMWindowType) {
properties2 |= NET::WM2TransientFor; // will be used when type is not set
}
if ((properties & NET::WMDesktop) && KX11Extras::mapViewport()) {
properties |= NET::WMGeometry; // for viewports, the desktop (workspace) is determined from the geometry
}
properties |= NET::XAWMState; // force to get error detection for valid()
d->m_info.reset(new NETWinInfo(QX11Info::connection(), d->window, QX11Info::appRootWindow(), properties, properties2));
if (properties & NET::WMName) {
if (d->m_info->name() && d->m_info->name()[0] != '\0') {
d->m_name = QString::fromUtf8(d->m_info->name());
} else {
d->m_name = KX11Extras::readNameProperty(d->window, XA_WM_NAME);
}
}
if (properties & NET::WMIconName) {
if (d->m_info->iconName() && d->m_info->iconName()[0] != '\0') {
d->m_iconic_name = QString::fromUtf8(d->m_info->iconName());
} else {
d->m_iconic_name = KX11Extras::readNameProperty(d->window, XA_WM_ICON_NAME);
}
}
if (properties & (NET::WMGeometry | NET::WMFrameExtents)) {
NETRect frame;
NETRect geom;
d->m_info->kdeGeometry(frame, geom);
d->m_geometry.setRect(geom.pos.x, geom.pos.y, geom.size.width, geom.size.height);
d->m_frame_geometry.setRect(frame.pos.x, frame.pos.y, frame.size.width, frame.size.height);
}
d->m_valid = !handler.error(false); // no sync - NETWinInfo did roundtrips
if (haveXRes()) {
xcb_res_client_id_spec_t specs;
specs.client = win();
specs.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
auto cookie = xcb_res_query_client_ids(QX11Info::connection(), 1, &specs);
UniqueCPointer<xcb_res_query_client_ids_reply_t> reply(xcb_res_query_client_ids_reply(QX11Info::connection(), cookie, nullptr));
if (reply && xcb_res_query_client_ids_ids_length(reply.get()) > 0) {
uint32_t pid = *xcb_res_client_id_value_value((xcb_res_query_client_ids_ids_iterator(reply.get()).data));
d->m_pid = pid;
}
}
}
KWindowInfo::KWindowInfo(const KWindowInfo &other)
: d(other.d)
{
}
KWindowInfo::~KWindowInfo()
{
}
KWindowInfo &KWindowInfo::operator=(const KWindowInfo &other)
{
if (d != other.d) {
d = other.d;
}
return *this;
}
bool KWindowInfo::valid(bool withdrawn_is_valid) const
{
if (!KWindowSystem::isPlatformX11()) {
return false;
}
if (!d->m_valid) {
return false;
}
if (!withdrawn_is_valid && mappingState() == NET::Withdrawn) {
return false;
}
return true;
}
WId KWindowInfo::win() const
{
return d->window;
}
#define CHECK_X11 \
if (!KWindowSystem::isPlatformX11()) { \
qCWarning(LOG_KWINDOWSYSTEM) << "KWindowInfo is only functional when running on X11"; \
return {}; \
}
NET::States KWindowInfo::state() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMState)) {
qWarning() << "Pass NET::WMState to KWindowInfo";
}
#endif
return d->m_info->state();
}
bool KWindowInfo::hasState(NET::States s) const
{
CHECK_X11
return (state() & s) == s;
}
bool KWindowInfo::icccmCompliantMappingState() const
{
CHECK_X11
static enum { noidea, yes, no } wm_is_1_2_compliant = noidea;
if (wm_is_1_2_compliant == noidea) {
NETRootInfo info(QX11Info::connection(), NET::Supported, NET::Properties2(), QX11Info::appScreen());
wm_is_1_2_compliant = info.isSupported(NET::Hidden) ? yes : no;
}
return wm_is_1_2_compliant == yes;
}
// see NETWM spec section 7.6
bool KWindowInfo::isMinimized() const
{
CHECK_X11
if (mappingState() != NET::Iconic) {
return false;
}
// NETWM 1.2 compliant WM - uses NET::Hidden for minimized windows
if ((state() & NET::Hidden) != 0 && (state() & NET::Shaded) == 0) { // shaded may have NET::Hidden too
return true;
}
// older WMs use WithdrawnState for other virtual desktops
// and IconicState only for minimized
return icccmCompliantMappingState() ? false : true;
}
NET::MappingState KWindowInfo::mappingState() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::XAWMState)) {
qWarning() << "Pass NET::XAWMState to KWindowInfo";
}
#endif
return d->m_info->mappingState();
}
NETExtendedStrut KWindowInfo::extendedStrut() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2ExtendedStrut)) {
qWarning() << "Pass NET::WM2ExtendedStrut to KWindowInfo";
}
#endif
NETExtendedStrut ext = d->m_info->extendedStrut();
NETStrut str = d->m_info->strut();
if (ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0
&& (str.left != 0 || str.right != 0 || str.top != 0 || str.bottom != 0)) {
// build extended from simple
if (str.left != 0) {
ext.left_width = str.left;
ext.left_start = 0;
ext.left_end = XDisplayHeight(QX11Info::display(), DefaultScreen(QX11Info::display()));
}
if (str.right != 0) {
ext.right_width = str.right;
ext.right_start = 0;
ext.right_end = XDisplayHeight(QX11Info::display(), DefaultScreen(QX11Info::display()));
}
if (str.top != 0) {
ext.top_width = str.top;
ext.top_start = 0;
ext.top_end = XDisplayWidth(QX11Info::display(), DefaultScreen(QX11Info::display()));
}
if (str.bottom != 0) {
ext.bottom_width = str.bottom;
ext.bottom_start = 0;
ext.bottom_end = XDisplayWidth(QX11Info::display(), DefaultScreen(QX11Info::display()));
}
}
return ext;
}
NET::WindowType KWindowInfo::windowType(NET::WindowTypes supported_types) const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMWindowType)) {
qWarning() << "Pass NET::WMWindowType to KWindowInfo";
}
#endif
if (!d->m_info->hasWindowType()) { // fallback, per spec recommendation
if (transientFor() != XCB_WINDOW_NONE) { // dialog
if (supported_types & NET::DialogMask) {
return NET::Dialog;
}
} else {
if (supported_types & NET::NormalMask) {
return NET::Normal;
}
}
}
return d->m_info->windowType(supported_types);
}
QString KWindowInfo::visibleName() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMVisibleName)) {
qWarning() << "Pass NET::WMVisibleName to KWindowInfo";
}
#endif
return d->m_info->visibleName() && d->m_info->visibleName()[0] != '\0' ? QString::fromUtf8(d->m_info->visibleName()) : name();
}
QString KWindowInfo::visibleNameWithState() const
{
CHECK_X11
QString s = visibleName();
if (isMinimized()) {
s.prepend(QLatin1Char('('));
s.append(QLatin1Char(')'));
}
return s;
}
QString KWindowInfo::name() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMName)) {
qWarning() << "Pass NET::WMName to KWindowInfo";
}
#endif
return d->m_name;
}
QString KWindowInfo::visibleIconName() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMVisibleIconName)) {
qWarning() << "Pass NET::WMVisibleIconName to KWindowInfo";
}
#endif
if (d->m_info->visibleIconName() && d->m_info->visibleIconName()[0] != '\0') {
return QString::fromUtf8(d->m_info->visibleIconName());
}
if (d->m_info->iconName() && d->m_info->iconName()[0] != '\0') {
return QString::fromUtf8(d->m_info->iconName());
}
if (!d->m_iconic_name.isEmpty()) {
return d->m_iconic_name;
}
return visibleName();
}
QString KWindowInfo::visibleIconNameWithState() const
{
CHECK_X11
QString s = visibleIconName();
if (isMinimized()) {
s.prepend(QLatin1Char('('));
s.append(QLatin1Char(')'));
}
return s;
}
QString KWindowInfo::iconName() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMIconName)) {
qWarning() << "Pass NET::WMIconName to KWindowInfo";
}
#endif
if (d->m_info->iconName() && d->m_info->iconName()[0] != '\0') {
return QString::fromUtf8(d->m_info->iconName());
}
if (!d->m_iconic_name.isEmpty()) {
return d->m_iconic_name;
}
return name();
}
bool KWindowInfo::isOnCurrentDesktop() const
{
CHECK_X11
return isOnDesktop(KX11Extras::currentDesktop());
}
bool KWindowInfo::isOnDesktop(int desktop) const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMDesktop)) {
qWarning() << "Pass NET::WMDesktop to KWindowInfo";
}
#endif
if (KX11Extras::mapViewport()) {
if (onAllDesktops()) {
return true;
}
return KX11Extras::viewportWindowToDesktop(d->m_geometry) == desktop;
}
return d->m_info->desktop() == desktop || d->m_info->desktop() == NET::OnAllDesktops;
}
bool KWindowInfo::onAllDesktops() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMDesktop)) {
qWarning() << "Pass NET::WMDesktop to KWindowInfo";
}
#endif
if (KX11Extras::mapViewport()) {
if (d->m_info->passedProperties() & NET::WMState) {
return d->m_info->state() & NET::Sticky;
}
NETWinInfo info(QX11Info::connection(), win(), QX11Info::appRootWindow(), NET::WMState, NET::Properties2());
return info.state() & NET::Sticky;
}
return d->m_info->desktop() == NET::OnAllDesktops;
}
int KWindowInfo::desktop() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMDesktop)) {
qWarning() << "Pass NET::WMDesktop to KWindowInfo";
}
#endif
if (KX11Extras::mapViewport()) {
if (onAllDesktops()) {
return NET::OnAllDesktops;
}
return KX11Extras::viewportWindowToDesktop(d->m_geometry);
}
return d->m_info->desktop();
}
QStringList KWindowInfo::activities() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2Activities)) {
qWarning() << "Pass NET::WM2Activities to KWindowInfo";
}
#endif
const QStringList result = QString::fromLatin1(d->m_info->activities()).split(QLatin1Char(','), Qt::SkipEmptyParts);
return result.contains(QStringLiteral(KDE_ALL_ACTIVITIES_UUID)) ? QStringList() : result;
}
QRect KWindowInfo::geometry() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMGeometry)) {
qWarning() << "Pass NET::WMGeometry to KWindowInfo";
}
#endif
return d->m_geometry;
}
QRect KWindowInfo::frameGeometry() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMFrameExtents)) {
qWarning() << "Pass NET::WMFrameExtents to KWindowInfo";
}
#endif
return d->m_frame_geometry;
}
WId KWindowInfo::transientFor() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2TransientFor)) {
qWarning() << "Pass NET::WM2TransientFor to KWindowInfo";
}
#endif
return d->m_info->transientFor();
}
WId KWindowInfo::groupLeader() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2GroupLeader)) {
qWarning() << "Pass NET::WM2GroupLeader to KWindowInfo";
}
#endif
return d->m_info->groupLeader();
}
QByteArray KWindowInfo::windowClassClass() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2WindowClass)) {
qWarning() << "Pass NET::WM2WindowClass to KWindowInfo";
}
#endif
return d->m_info->windowClassClass();
}
QByteArray KWindowInfo::windowClassName() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2WindowClass)) {
qWarning() << "Pass NET::WM2WindowClass to KWindowInfo";
}
#endif
return d->m_info->windowClassName();
}
QByteArray KWindowInfo::windowRole() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2WindowRole)) {
qWarning() << "Pass NET::WM2WindowRole to KWindowInfo";
}
#endif
return d->m_info->windowRole();
}
QByteArray KWindowInfo::clientMachine() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2ClientMachine)) {
qWarning() << "Pass NET::WM2ClientMachine to KWindowInfo";
}
#endif
return d->m_info->clientMachine();
}
bool KWindowInfo::allowedActionsSupported() const
{
CHECK_X11
static enum { noidea, yes, no } wm_supports_allowed_actions = noidea;
if (wm_supports_allowed_actions == noidea) {
NETRootInfo info(QX11Info::connection(), NET::Supported, NET::Properties2(), QX11Info::appScreen());
wm_supports_allowed_actions = info.isSupported(NET::WM2AllowedActions) ? yes : no;
}
return wm_supports_allowed_actions == yes;
}
bool KWindowInfo::actionSupported(NET::Action action) const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2AllowedActions)) {
qWarning() << "Pass NET::WM2AllowedActions to KWindowInfo";
}
#endif
if (allowedActionsSupported()) {
return d->m_info->allowedActions() & action;
} else {
return true; // no idea if it's supported or not -> pretend it is
}
}
QByteArray KWindowInfo::desktopFileName() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2DesktopFileName)) {
qWarning() << "Pass NET::WM2DesktopFileName to KWindowInfo";
}
#endif
return QByteArray(d->m_info->desktopFileName());
}
QByteArray KWindowInfo::gtkApplicationId() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2DesktopFileName)) {
qWarning() << "Pass NET::WM2DesktopFileName to KWindowInfo";
}
#endif
return QByteArray(d->m_info->gtkApplicationId());
}
QByteArray KWindowInfo::applicationMenuServiceName() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2AppMenuServiceName)) {
qWarning() << "Pass NET::WM2AppMenuServiceName to KWindowInfo";
}
#endif
return QByteArray(d->m_info->appMenuServiceName());
}
QByteArray KWindowInfo::applicationMenuObjectPath() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2AppMenuObjectPath)) {
qWarning() << "Pass NET::WM2AppMenuObjectPath to KWindowInfo";
}
#endif
return QByteArray(d->m_info->appMenuObjectPath());
}
int KWindowInfo::pid() const
{
CHECK_X11
// If pid is found using the XRes extension use that instead.
// It is more reliable than the app reporting it's own PID as apps
// within an app namespace are unable to do so correctly
if (d->m_pid > 0) {
return d->m_pid;
}
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMPid)) {
qWarning() << "Pass NET::WMPid to KWindowInfo";
}
#endif
return d->m_info->pid();
}