File indexing completed on 2024-05-05 12:24:29
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 1999 Matthias Ettrich <ettrich@kde.org> 0004 SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org> 0005 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.1-or-later 0008 */ 0009 0010 #include "kwindowinfo_p_x11.h" 0011 #include "kwindowsystem.h" 0012 #include "kx11extras.h" 0013 0014 #include <QDebug> 0015 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0016 #include <private/qtx11extras_p.h> 0017 #else 0018 #include <QX11Info> 0019 #endif 0020 0021 #include <X11/Xatom.h> 0022 #include <kxerrorhandler_p.h> 0023 #include <netwm.h> 0024 0025 #include <xcb/res.h> 0026 0027 #include "cptr_p.h" 0028 0029 static bool haveXRes() 0030 { 0031 static bool s_checked = false; 0032 static bool s_haveXRes = false; 0033 if (!s_checked) { 0034 auto cookie = xcb_res_query_version(QX11Info::connection(), XCB_RES_MAJOR_VERSION, XCB_RES_MINOR_VERSION); 0035 UniqueCPointer<xcb_res_query_version_reply_t> reply(xcb_res_query_version_reply(QX11Info::connection(), cookie, nullptr)); 0036 s_haveXRes = reply != nullptr; 0037 s_checked = true; 0038 } 0039 return s_haveXRes; 0040 } 0041 0042 // KWindowSystem::info() should be updated too if something has to be changed here 0043 KWindowInfoPrivateX11::KWindowInfoPrivateX11(WId _win, NET::Properties properties, NET::Properties2 properties2) 0044 : KWindowInfoPrivate(_win, properties, properties2) 0045 , KWindowInfoPrivateDesktopFileNameExtension() 0046 , KWindowInfoPrivatePidExtension() 0047 , KWindowInfoPrivateAppMenuExtension() 0048 { 0049 installDesktopFileNameExtension(this); 0050 installPidExtension(this); 0051 installAppMenuExtension(this); 0052 installGtkApplicationIdExtension(this); 0053 0054 KXErrorHandler handler; 0055 if (properties & NET::WMVisibleIconName) { 0056 properties |= NET::WMIconName | NET::WMVisibleName; // force, in case it will be used as a fallback 0057 } 0058 if (properties & NET::WMVisibleName) { 0059 properties |= NET::WMName; // force, in case it will be used as a fallback 0060 } 0061 if (properties2 & NET::WM2ExtendedStrut) { 0062 properties |= NET::WMStrut; // will be used as fallback 0063 } 0064 if (properties & NET::WMWindowType) { 0065 properties2 |= NET::WM2TransientFor; // will be used when type is not set 0066 } 0067 if ((properties & NET::WMDesktop) && KX11Extras::mapViewport()) { 0068 properties |= NET::WMGeometry; // for viewports, the desktop (workspace) is determined from the geometry 0069 } 0070 properties |= NET::XAWMState; // force to get error detection for valid() 0071 m_info.reset(new NETWinInfo(QX11Info::connection(), _win, QX11Info::appRootWindow(), properties, properties2)); 0072 if (properties & NET::WMName) { 0073 if (m_info->name() && m_info->name()[0] != '\0') { 0074 m_name = QString::fromUtf8(m_info->name()); 0075 } else { 0076 m_name = KX11Extras::readNameProperty(_win, XA_WM_NAME); 0077 } 0078 } 0079 if (properties & NET::WMIconName) { 0080 if (m_info->iconName() && m_info->iconName()[0] != '\0') { 0081 m_iconic_name = QString::fromUtf8(m_info->iconName()); 0082 } else { 0083 m_iconic_name = KX11Extras::readNameProperty(_win, XA_WM_ICON_NAME); 0084 } 0085 } 0086 if (properties & (NET::WMGeometry | NET::WMFrameExtents)) { 0087 NETRect frame; 0088 NETRect geom; 0089 m_info->kdeGeometry(frame, geom); 0090 m_geometry.setRect(geom.pos.x, geom.pos.y, geom.size.width, geom.size.height); 0091 m_frame_geometry.setRect(frame.pos.x, frame.pos.y, frame.size.width, frame.size.height); 0092 } 0093 m_valid = !handler.error(false); // no sync - NETWinInfo did roundtrips 0094 0095 if (haveXRes()) { 0096 xcb_res_client_id_spec_t specs; 0097 specs.client = win(); 0098 specs.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; 0099 auto cookie = xcb_res_query_client_ids(QX11Info::connection(), 1, &specs); 0100 0101 UniqueCPointer<xcb_res_query_client_ids_reply_t> reply(xcb_res_query_client_ids_reply(QX11Info::connection(), cookie, nullptr)); 0102 if (reply && xcb_res_query_client_ids_ids_length(reply.get()) > 0) { 0103 uint32_t pid = *xcb_res_client_id_value_value((xcb_res_query_client_ids_ids_iterator(reply.get()).data)); 0104 m_pid = pid; 0105 } 0106 } 0107 } 0108 0109 KWindowInfoPrivateX11::~KWindowInfoPrivateX11() 0110 { 0111 } 0112 0113 bool KWindowInfoPrivateX11::valid(bool withdrawn_is_valid) const 0114 { 0115 if (!m_valid) { 0116 return false; 0117 } 0118 if (!withdrawn_is_valid && mappingState() == NET::Withdrawn) { 0119 return false; 0120 } 0121 return true; 0122 } 0123 0124 NET::States KWindowInfoPrivateX11::state() const 0125 { 0126 #if !defined(KDE_NO_WARNING_OUTPUT) 0127 if (!(m_info->passedProperties() & NET::WMState)) { 0128 qWarning() << "Pass NET::WMState to KWindowInfo"; 0129 } 0130 #endif 0131 return m_info->state(); 0132 } 0133 0134 NET::MappingState KWindowInfoPrivateX11::mappingState() const 0135 { 0136 #if !defined(KDE_NO_WARNING_OUTPUT) 0137 if (!(m_info->passedProperties() & NET::XAWMState)) { 0138 qWarning() << "Pass NET::XAWMState to KWindowInfo"; 0139 } 0140 #endif 0141 return m_info->mappingState(); 0142 } 0143 0144 NETExtendedStrut KWindowInfoPrivateX11::extendedStrut() const 0145 { 0146 #if !defined(KDE_NO_WARNING_OUTPUT) 0147 if (!(m_info->passedProperties2() & NET::WM2ExtendedStrut)) { 0148 qWarning() << "Pass NET::WM2ExtendedStrut to KWindowInfo"; 0149 } 0150 #endif 0151 NETExtendedStrut ext = m_info->extendedStrut(); 0152 NETStrut str = m_info->strut(); 0153 if (ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0 0154 && (str.left != 0 || str.right != 0 || str.top != 0 || str.bottom != 0)) { 0155 // build extended from simple 0156 if (str.left != 0) { 0157 ext.left_width = str.left; 0158 ext.left_start = 0; 0159 ext.left_end = XDisplayHeight(QX11Info::display(), DefaultScreen(QX11Info::display())); 0160 } 0161 if (str.right != 0) { 0162 ext.right_width = str.right; 0163 ext.right_start = 0; 0164 ext.right_end = XDisplayHeight(QX11Info::display(), DefaultScreen(QX11Info::display())); 0165 } 0166 if (str.top != 0) { 0167 ext.top_width = str.top; 0168 ext.top_start = 0; 0169 ext.top_end = XDisplayWidth(QX11Info::display(), DefaultScreen(QX11Info::display())); 0170 } 0171 if (str.bottom != 0) { 0172 ext.bottom_width = str.bottom; 0173 ext.bottom_start = 0; 0174 ext.bottom_end = XDisplayWidth(QX11Info::display(), DefaultScreen(QX11Info::display())); 0175 } 0176 } 0177 return ext; 0178 } 0179 0180 NET::WindowType KWindowInfoPrivateX11::windowType(NET::WindowTypes supported_types) const 0181 { 0182 #if !defined(KDE_NO_WARNING_OUTPUT) 0183 if (!(m_info->passedProperties() & NET::WMWindowType)) { 0184 qWarning() << "Pass NET::WMWindowType to KWindowInfo"; 0185 } 0186 #endif 0187 if (!m_info->hasWindowType()) { // fallback, per spec recommendation 0188 if (transientFor() != XCB_WINDOW_NONE) { // dialog 0189 if (supported_types & NET::DialogMask) { 0190 return NET::Dialog; 0191 } 0192 } else { 0193 if (supported_types & NET::NormalMask) { 0194 return NET::Normal; 0195 } 0196 } 0197 } 0198 return m_info->windowType(supported_types); 0199 } 0200 0201 QString KWindowInfoPrivateX11::visibleNameWithState() const 0202 { 0203 QString s = visibleName(); 0204 if (isMinimized()) { 0205 s.prepend(QLatin1Char('(')); 0206 s.append(QLatin1Char(')')); 0207 } 0208 return s; 0209 } 0210 0211 QString KWindowInfoPrivateX11::visibleName() const 0212 { 0213 #if !defined(KDE_NO_WARNING_OUTPUT) 0214 if (!(m_info->passedProperties() & NET::WMVisibleName)) { 0215 qWarning() << "Pass NET::WMVisibleName to KWindowInfo"; 0216 } 0217 #endif 0218 return m_info->visibleName() && m_info->visibleName()[0] != '\0' ? QString::fromUtf8(m_info->visibleName()) : name(); 0219 } 0220 0221 QString KWindowInfoPrivateX11::name() const 0222 { 0223 #if !defined(KDE_NO_WARNING_OUTPUT) 0224 if (!(m_info->passedProperties() & NET::WMName)) { 0225 qWarning() << "Pass NET::WMName to KWindowInfo"; 0226 } 0227 #endif 0228 return m_name; 0229 } 0230 0231 QString KWindowInfoPrivateX11::visibleIconNameWithState() const 0232 { 0233 QString s = visibleIconName(); 0234 if (isMinimized()) { 0235 s.prepend(QLatin1Char('(')); 0236 s.append(QLatin1Char(')')); 0237 } 0238 return s; 0239 } 0240 0241 QString KWindowInfoPrivateX11::visibleIconName() const 0242 { 0243 #if !defined(KDE_NO_WARNING_OUTPUT) 0244 if (!(m_info->passedProperties() & NET::WMVisibleIconName)) { 0245 qWarning() << "Pass NET::WMVisibleIconName to KWindowInfo"; 0246 } 0247 #endif 0248 if (m_info->visibleIconName() && m_info->visibleIconName()[0] != '\0') { 0249 return QString::fromUtf8(m_info->visibleIconName()); 0250 } 0251 if (m_info->iconName() && m_info->iconName()[0] != '\0') { 0252 return QString::fromUtf8(m_info->iconName()); 0253 } 0254 if (!m_iconic_name.isEmpty()) { 0255 return m_iconic_name; 0256 } 0257 return visibleName(); 0258 } 0259 0260 QString KWindowInfoPrivateX11::iconName() const 0261 { 0262 #if !defined(KDE_NO_WARNING_OUTPUT) 0263 if (!(m_info->passedProperties() & NET::WMIconName)) { 0264 qWarning() << "Pass NET::WMIconName to KWindowInfo"; 0265 } 0266 #endif 0267 if (m_info->iconName() && m_info->iconName()[0] != '\0') { 0268 return QString::fromUtf8(m_info->iconName()); 0269 } 0270 if (!m_iconic_name.isEmpty()) { 0271 return m_iconic_name; 0272 } 0273 return name(); 0274 } 0275 0276 bool KWindowInfoPrivateX11::isOnDesktop(int _desktop) const 0277 { 0278 #if !defined(KDE_NO_WARNING_OUTPUT) 0279 if (!(m_info->passedProperties() & NET::WMDesktop)) { 0280 qWarning() << "Pass NET::WMDesktop to KWindowInfo"; 0281 } 0282 #endif 0283 if (KX11Extras::mapViewport()) { 0284 if (onAllDesktops()) { 0285 return true; 0286 } 0287 return KWindowSystem::viewportWindowToDesktop(m_geometry) == _desktop; 0288 } 0289 return m_info->desktop() == _desktop || m_info->desktop() == NET::OnAllDesktops; 0290 } 0291 0292 bool KWindowInfoPrivateX11::onAllDesktops() const 0293 { 0294 #if !defined(KDE_NO_WARNING_OUTPUT) 0295 if (!(m_info->passedProperties() & NET::WMDesktop)) { 0296 qWarning() << "Pass NET::WMDesktop to KWindowInfo"; 0297 } 0298 #endif 0299 if (KX11Extras::mapViewport()) { 0300 if (m_info->passedProperties() & NET::WMState) { 0301 return m_info->state() & NET::Sticky; 0302 } 0303 NETWinInfo info(QX11Info::connection(), win(), QX11Info::appRootWindow(), NET::WMState, NET::Properties2()); 0304 return info.state() & NET::Sticky; 0305 } 0306 return m_info->desktop() == NET::OnAllDesktops; 0307 } 0308 0309 int KWindowInfoPrivateX11::desktop() const 0310 { 0311 #if !defined(KDE_NO_WARNING_OUTPUT) 0312 if (!(m_info->passedProperties() & NET::WMDesktop)) { 0313 qWarning() << "Pass NET::WMDesktop to KWindowInfo"; 0314 } 0315 #endif 0316 if (KX11Extras::mapViewport()) { 0317 if (onAllDesktops()) { 0318 return NET::OnAllDesktops; 0319 } 0320 return KWindowSystem::viewportWindowToDesktop(m_geometry); 0321 } 0322 return m_info->desktop(); 0323 } 0324 0325 QStringList KWindowInfoPrivateX11::activities() const 0326 { 0327 #if !defined(KDE_NO_WARNING_OUTPUT) 0328 if (!(m_info->passedProperties2() & NET::WM2Activities)) { 0329 qWarning() << "Pass NET::WM2Activities to KWindowInfo"; 0330 } 0331 #endif 0332 0333 const QStringList result = QString::fromLatin1(m_info->activities()).split(QLatin1Char(','), Qt::SkipEmptyParts); 0334 0335 return result.contains(QStringLiteral(KDE_ALL_ACTIVITIES_UUID)) ? QStringList() : result; 0336 } 0337 0338 QRect KWindowInfoPrivateX11::geometry() const 0339 { 0340 #if !defined(KDE_NO_WARNING_OUTPUT) 0341 if (!(m_info->passedProperties() & NET::WMGeometry)) { 0342 qWarning() << "Pass NET::WMGeometry to KWindowInfo"; 0343 } 0344 #endif 0345 return m_geometry; 0346 } 0347 0348 QRect KWindowInfoPrivateX11::frameGeometry() const 0349 { 0350 #if !defined(KDE_NO_WARNING_OUTPUT) 0351 if (!(m_info->passedProperties() & NET::WMFrameExtents)) { 0352 qWarning() << "Pass NET::WMFrameExtents to KWindowInfo"; 0353 } 0354 #endif 0355 return m_frame_geometry; 0356 } 0357 0358 WId KWindowInfoPrivateX11::transientFor() const 0359 { 0360 #if !defined(KDE_NO_WARNING_OUTPUT) 0361 if (!(m_info->passedProperties2() & NET::WM2TransientFor)) { 0362 qWarning() << "Pass NET::WM2TransientFor to KWindowInfo"; 0363 } 0364 #endif 0365 return m_info->transientFor(); 0366 } 0367 0368 WId KWindowInfoPrivateX11::groupLeader() const 0369 { 0370 #if !defined(KDE_NO_WARNING_OUTPUT) 0371 if (!(m_info->passedProperties2() & NET::WM2GroupLeader)) { 0372 qWarning() << "Pass NET::WM2GroupLeader to KWindowInfo"; 0373 } 0374 #endif 0375 return m_info->groupLeader(); 0376 } 0377 0378 QByteArray KWindowInfoPrivateX11::windowClassClass() const 0379 { 0380 #if !defined(KDE_NO_WARNING_OUTPUT) 0381 if (!(m_info->passedProperties2() & NET::WM2WindowClass)) { 0382 qWarning() << "Pass NET::WM2WindowClass to KWindowInfo"; 0383 } 0384 #endif 0385 return m_info->windowClassClass(); 0386 } 0387 0388 QByteArray KWindowInfoPrivateX11::windowClassName() const 0389 { 0390 #if !defined(KDE_NO_WARNING_OUTPUT) 0391 if (!(m_info->passedProperties2() & NET::WM2WindowClass)) { 0392 qWarning() << "Pass NET::WM2WindowClass to KWindowInfo"; 0393 } 0394 #endif 0395 return m_info->windowClassName(); 0396 } 0397 0398 QByteArray KWindowInfoPrivateX11::windowRole() const 0399 { 0400 #if !defined(KDE_NO_WARNING_OUTPUT) 0401 if (!(m_info->passedProperties2() & NET::WM2WindowRole)) { 0402 qWarning() << "Pass NET::WM2WindowRole to KWindowInfo"; 0403 } 0404 #endif 0405 return m_info->windowRole(); 0406 } 0407 0408 QByteArray KWindowInfoPrivateX11::clientMachine() const 0409 { 0410 #if !defined(KDE_NO_WARNING_OUTPUT) 0411 if (!(m_info->passedProperties2() & NET::WM2ClientMachine)) { 0412 qWarning() << "Pass NET::WM2ClientMachine to KWindowInfo"; 0413 } 0414 #endif 0415 return m_info->clientMachine(); 0416 } 0417 0418 bool KWindowInfoPrivateX11::actionSupported(NET::Action action) const 0419 { 0420 #if !defined(KDE_NO_WARNING_OUTPUT) 0421 if (!(m_info->passedProperties2() & NET::WM2AllowedActions)) { 0422 qWarning() << "Pass NET::WM2AllowedActions to KWindowInfo"; 0423 } 0424 #endif 0425 if (KWindowSystem::allowedActionsSupported()) { 0426 return m_info->allowedActions() & action; 0427 } else { 0428 return true; // no idea if it's supported or not -> pretend it is 0429 } 0430 } 0431 0432 bool KWindowInfoPrivateX11::icccmCompliantMappingState() const 0433 { 0434 static enum { noidea, yes, no } wm_is_1_2_compliant = noidea; 0435 if (wm_is_1_2_compliant == noidea) { 0436 NETRootInfo info(QX11Info::connection(), NET::Supported, NET::Properties2(), QX11Info::appScreen()); 0437 wm_is_1_2_compliant = info.isSupported(NET::Hidden) ? yes : no; 0438 } 0439 return wm_is_1_2_compliant == yes; 0440 } 0441 0442 // see NETWM spec section 7.6 0443 bool KWindowInfoPrivateX11::isMinimized() const 0444 { 0445 if (mappingState() != NET::Iconic) { 0446 return false; 0447 } 0448 // NETWM 1.2 compliant WM - uses NET::Hidden for minimized windows 0449 if ((state() & NET::Hidden) != 0 && (state() & NET::Shaded) == 0) { // shaded may have NET::Hidden too 0450 return true; 0451 } 0452 // older WMs use WithdrawnState for other virtual desktops 0453 // and IconicState only for minimized 0454 return icccmCompliantMappingState() ? false : true; 0455 } 0456 0457 QByteArray KWindowInfoPrivateX11::desktopFileName() const 0458 { 0459 #if !defined(KDE_NO_WARNING_OUTPUT) 0460 if (!(m_info->passedProperties2() & NET::WM2DesktopFileName)) { 0461 qWarning() << "Pass NET::WM2DesktopFileName to KWindowInfo"; 0462 } 0463 #endif 0464 return QByteArray(m_info->desktopFileName()); 0465 } 0466 0467 QByteArray KWindowInfoPrivateX11::gtkApplicationId() const 0468 { 0469 #if !defined(KDE_NO_WARNING_OUTPUT) 0470 if (!(m_info->passedProperties2() & NET::WM2DesktopFileName)) { 0471 qWarning() << "Pass NET::WM2DesktopFileName to KWindowInfo"; 0472 } 0473 #endif 0474 return QByteArray(m_info->gtkApplicationId()); 0475 } 0476 0477 QByteArray KWindowInfoPrivateX11::applicationMenuObjectPath() const 0478 { 0479 #if !defined(KDE_NO_WARNING_OUTPUT) 0480 if (!(m_info->passedProperties2() & NET::WM2AppMenuObjectPath)) { 0481 qWarning() << "Pass NET::WM2AppMenuObjectPath to KWindowInfo"; 0482 } 0483 #endif 0484 return QByteArray(m_info->appMenuObjectPath()); 0485 } 0486 0487 QByteArray KWindowInfoPrivateX11::applicationMenuServiceName() const 0488 { 0489 #if !defined(KDE_NO_WARNING_OUTPUT) 0490 if (!(m_info->passedProperties2() & NET::WM2AppMenuServiceName)) { 0491 qWarning() << "Pass NET::WM2AppMenuServiceName to KWindowInfo"; 0492 } 0493 #endif 0494 return QByteArray(m_info->appMenuServiceName()); 0495 } 0496 0497 int KWindowInfoPrivateX11::pid() const 0498 { 0499 // If pid is found using the XRes extension use that instead. 0500 // It is more reliable than the app reporting it's own PID as apps 0501 // within an app namespace are unable to do so correctly 0502 if (m_pid > 0) { 0503 return m_pid; 0504 } 0505 0506 #if !defined(KDE_NO_WARNING_OUTPUT) 0507 if (!(m_info->passedProperties() & NET::WMPid)) { 0508 qWarning() << "Pass NET::WMPid to KWindowInfo"; 0509 } 0510 #endif 0511 0512 return m_info->pid(); 0513 }