File indexing completed on 2024-05-05 12:24:30
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 "kwindowsystem.h" 0011 #include "kwindowsystem_p_x11.h" 0012 0013 #include "cptr_p.h" 0014 0015 // clang-format off 0016 #include <kxerrorhandler_p.h> 0017 #include <fixx11h.h> 0018 #include <kxutils_p.h> 0019 // clang-format on 0020 0021 #include <QGuiApplication> 0022 #include <QIcon> 0023 #include <QMetaMethod> 0024 #include <QScreen> 0025 #include <QWindow> 0026 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0027 #include <private/qtx11extras_p.h> 0028 #else 0029 #include <QX11Info> 0030 #endif 0031 0032 #include <X11/Xatom.h> 0033 #include <X11/Xutil.h> 0034 #include <xcb/xcb.h> 0035 #include <xcb/xfixes.h> 0036 0037 #include <config-kwindowsystem.h> 0038 0039 #if KWINDOWSYSTEM_HAVE_XFIXES 0040 #include <X11/extensions/Xfixes.h> 0041 #endif 0042 0043 #include "kx11extras.h" 0044 0045 static Atom net_wm_cm; 0046 static void create_atoms(); 0047 0048 static inline const QRect &displayGeometry() 0049 { 0050 static QRect displayGeometry; 0051 static bool isDirty = true; 0052 0053 if (isDirty) { 0054 static QList<QMetaObject::Connection> connections; 0055 auto dirtify = [&] { 0056 isDirty = true; 0057 for (const QMetaObject::Connection &con : std::as_const(connections)) { 0058 QObject::disconnect(con); 0059 } 0060 connections.clear(); 0061 }; 0062 0063 QObject::connect(qApp, &QGuiApplication::screenAdded, dirtify); 0064 QObject::connect(qApp, &QGuiApplication::screenRemoved, dirtify); 0065 const QList<QScreen *> screenList = QGuiApplication::screens(); 0066 QRegion region; 0067 for (int i = 0; i < screenList.count(); ++i) { 0068 const QScreen *screen = screenList.at(i); 0069 connections << QObject::connect(screen, &QScreen::geometryChanged, dirtify); 0070 const QRect geometry = screen->geometry(); 0071 const qreal dpr = screen->devicePixelRatio(); 0072 region += QRect(geometry.topLeft(), geometry.size() * dpr); 0073 } 0074 displayGeometry = region.boundingRect(); 0075 isDirty = false; 0076 } 0077 0078 return displayGeometry; 0079 } 0080 0081 static inline int displayWidth() 0082 { 0083 return displayGeometry().width(); 0084 } 0085 0086 static inline int displayHeight() 0087 { 0088 return displayGeometry().height(); 0089 } 0090 0091 // clang-format off 0092 static const NET::Properties windowsProperties = NET::ClientList | NET::ClientListStacking | 0093 NET::Supported | 0094 NET::NumberOfDesktops | 0095 NET::DesktopGeometry | 0096 NET::DesktopViewport | 0097 NET::CurrentDesktop | 0098 NET::DesktopNames | 0099 NET::ActiveWindow | 0100 NET::WorkArea; 0101 static const NET::Properties2 windowsProperties2 = NET::WM2ShowingDesktop; 0102 0103 // ClientList and ClientListStacking is not per-window information, but a desktop information, 0104 // so track it even with only INFO_BASIC 0105 static const NET::Properties desktopProperties = NET::ClientList | NET::ClientListStacking | 0106 NET::Supported | 0107 NET::NumberOfDesktops | 0108 NET::DesktopGeometry | 0109 NET::DesktopViewport | 0110 NET::CurrentDesktop | 0111 NET::DesktopNames | 0112 NET::ActiveWindow | 0113 NET::WorkArea; 0114 static const NET::Properties2 desktopProperties2 = NET::WM2ShowingDesktop; 0115 // clang-format on 0116 0117 MainThreadInstantiator::MainThreadInstantiator(KWindowSystemPrivateX11::FilterInfo _what) 0118 : QObject() 0119 , m_what(_what) 0120 { 0121 } 0122 0123 NETEventFilter *MainThreadInstantiator::createNETEventFilter() 0124 { 0125 return new NETEventFilter(m_what); 0126 } 0127 0128 NETEventFilter::NETEventFilter(KWindowSystemPrivateX11::FilterInfo _what) 0129 : NETRootInfo(QX11Info::connection(), 0130 _what >= KWindowSystemPrivateX11::INFO_WINDOWS ? windowsProperties : desktopProperties, 0131 _what >= KWindowSystemPrivateX11::INFO_WINDOWS ? windowsProperties2 : desktopProperties2, 0132 QX11Info::appScreen(), 0133 false) 0134 , QAbstractNativeEventFilter() 0135 , strutSignalConnected(false) 0136 , compositingEnabled(false) 0137 , haveXfixes(false) 0138 , what(_what) 0139 , winId(XCB_WINDOW_NONE) 0140 , m_appRootWindow(QX11Info::appRootWindow()) 0141 { 0142 QCoreApplication::instance()->installNativeEventFilter(this); 0143 0144 #if KWINDOWSYSTEM_HAVE_XFIXES 0145 int errorBase; 0146 if ((haveXfixes = XFixesQueryExtension(QX11Info::display(), &xfixesEventBase, &errorBase))) { 0147 create_atoms(); 0148 winId = xcb_generate_id(QX11Info::connection()); 0149 uint32_t values[] = {true, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY}; 0150 xcb_create_window(QX11Info::connection(), 0151 XCB_COPY_FROM_PARENT, 0152 winId, 0153 m_appRootWindow, 0154 0, 0155 0, 0156 1, 0157 1, 0158 0, 0159 XCB_WINDOW_CLASS_INPUT_ONLY, 0160 XCB_COPY_FROM_PARENT, 0161 XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK, 0162 values); 0163 XFixesSelectSelectionInput(QX11Info::display(), 0164 winId, 0165 net_wm_cm, 0166 XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask); 0167 compositingEnabled = XGetSelectionOwner(QX11Info::display(), net_wm_cm) != None; 0168 } 0169 #endif 0170 } 0171 0172 NETEventFilter::~NETEventFilter() 0173 { 0174 if (QX11Info::connection() && winId != XCB_WINDOW_NONE) { 0175 xcb_destroy_window(QX11Info::connection(), winId); 0176 winId = XCB_WINDOW_NONE; 0177 } 0178 } 0179 0180 // not virtual, but it's called directly only from init() 0181 void NETEventFilter::activate() 0182 { 0183 NETRootInfo::activate(); 0184 updateStackingOrder(); 0185 } 0186 0187 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0188 bool NETEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) 0189 #else 0190 bool NETEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *) 0191 #endif 0192 { 0193 if (eventType != "xcb_generic_event_t") { 0194 // only interested in XCB events of course 0195 return false; 0196 } 0197 return nativeEventFilter(reinterpret_cast<xcb_generic_event_t *>(message)); 0198 } 0199 0200 bool NETEventFilter::nativeEventFilter(xcb_generic_event_t *ev) 0201 { 0202 KWindowSystem *s_q = KWindowSystem::self(); 0203 const uint8_t eventType = ev->response_type & ~0x80; 0204 0205 if (eventType == xfixesEventBase + XCB_XFIXES_SELECTION_NOTIFY) { 0206 xcb_xfixes_selection_notify_event_t *event = reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(ev); 0207 if (event->window == winId) { 0208 bool haveOwner = event->owner != XCB_WINDOW_NONE; 0209 if (compositingEnabled != haveOwner) { 0210 compositingEnabled = haveOwner; 0211 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0212 Q_EMIT s_q->compositingChanged(compositingEnabled); 0213 #endif 0214 Q_EMIT KX11Extras::self()->compositingChanged(compositingEnabled); 0215 } 0216 return true; 0217 } 0218 // Qt compresses XFixesSelectionNotifyEvents without caring about the actual window 0219 // gui/kernel/qapplication_x11.cpp 0220 // until that can be assumed fixed, we also react on events on the root (caused by Qts own compositing tracker) 0221 if (event->window == m_appRootWindow) { 0222 if (event->selection == net_wm_cm) { 0223 bool haveOwner = event->owner != XCB_WINDOW_NONE; 0224 if (compositingEnabled != haveOwner) { 0225 compositingEnabled = haveOwner; 0226 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0227 Q_EMIT s_q->compositingChanged(compositingEnabled); 0228 #endif 0229 Q_EMIT KX11Extras::self()->compositingChanged(compositingEnabled); 0230 } 0231 // NOTICE this is not our event, we just randomly captured it from Qt -> pass on 0232 return false; 0233 } 0234 } 0235 return false; 0236 } 0237 0238 xcb_window_t eventWindow = XCB_WINDOW_NONE; 0239 switch (eventType) { 0240 case XCB_CLIENT_MESSAGE: 0241 eventWindow = reinterpret_cast<xcb_client_message_event_t *>(ev)->window; 0242 break; 0243 case XCB_PROPERTY_NOTIFY: 0244 eventWindow = reinterpret_cast<xcb_property_notify_event_t *>(ev)->window; 0245 break; 0246 case XCB_CONFIGURE_NOTIFY: 0247 eventWindow = reinterpret_cast<xcb_configure_notify_event_t *>(ev)->window; 0248 break; 0249 } 0250 0251 if (eventWindow == m_appRootWindow) { 0252 int old_current_desktop = currentDesktop(); 0253 xcb_window_t old_active_window = activeWindow(); 0254 int old_number_of_desktops = numberOfDesktops(); 0255 bool old_showing_desktop = showingDesktop(); 0256 NET::Properties props; 0257 NET::Properties2 props2; 0258 NETRootInfo::event(ev, &props, &props2); 0259 0260 if ((props & CurrentDesktop) && currentDesktop() != old_current_desktop) { 0261 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0262 Q_EMIT s_q->currentDesktopChanged(currentDesktop()); 0263 #endif 0264 Q_EMIT KX11Extras::self()->currentDesktopChanged(currentDesktop()); 0265 } 0266 if ((props & DesktopViewport) && mapViewport() && currentDesktop() != old_current_desktop) { 0267 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0268 Q_EMIT s_q->currentDesktopChanged(currentDesktop()); 0269 #endif 0270 Q_EMIT KX11Extras::self()->currentDesktopChanged(currentDesktop()); 0271 } 0272 if ((props & ActiveWindow) && activeWindow() != old_active_window) { 0273 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0274 Q_EMIT s_q->activeWindowChanged(activeWindow()); 0275 #endif 0276 Q_EMIT KX11Extras::self()->activeWindowChanged(activeWindow()); 0277 } 0278 if (props & DesktopNames) { 0279 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0280 Q_EMIT s_q->desktopNamesChanged(); 0281 #endif 0282 Q_EMIT KX11Extras::self()->desktopNamesChanged(); 0283 } 0284 if ((props & NumberOfDesktops) && numberOfDesktops() != old_number_of_desktops) { 0285 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0286 Q_EMIT s_q->numberOfDesktopsChanged(numberOfDesktops()); 0287 #endif 0288 Q_EMIT KX11Extras::self()->numberOfDesktopsChanged(numberOfDesktops()); 0289 } 0290 if ((props & DesktopGeometry) && mapViewport() && numberOfDesktops() != old_number_of_desktops) { 0291 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0292 Q_EMIT s_q->numberOfDesktopsChanged(numberOfDesktops()); 0293 #endif 0294 Q_EMIT KX11Extras::self()->numberOfDesktopsChanged(numberOfDesktops()); 0295 } 0296 if (props & WorkArea) { 0297 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0298 Q_EMIT s_q->workAreaChanged(); 0299 #endif 0300 Q_EMIT KX11Extras::self()->workAreaChanged(); 0301 } 0302 if (props & ClientListStacking) { 0303 updateStackingOrder(); 0304 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0305 Q_EMIT s_q->stackingOrderChanged(); 0306 #endif 0307 Q_EMIT KX11Extras::self()->stackingOrderChanged(); 0308 } 0309 if ((props2 & WM2ShowingDesktop) && showingDesktop() != old_showing_desktop) { 0310 Q_EMIT s_q->showingDesktopChanged(showingDesktop()); 0311 } 0312 } else if (windows.contains(eventWindow)) { 0313 NETWinInfo ni(QX11Info::connection(), eventWindow, m_appRootWindow, NET::Properties(), NET::Properties2()); 0314 NET::Properties dirtyProperties; 0315 NET::Properties2 dirtyProperties2; 0316 ni.event(ev, &dirtyProperties, &dirtyProperties2); 0317 if (eventType == XCB_PROPERTY_NOTIFY) { 0318 xcb_property_notify_event_t *event = reinterpret_cast<xcb_property_notify_event_t *>(ev); 0319 if (event->atom == XCB_ATOM_WM_HINTS) { 0320 dirtyProperties |= NET::WMIcon; // support for old icons 0321 } else if (event->atom == XCB_ATOM_WM_NAME) { 0322 dirtyProperties |= NET::WMName; // support for old name 0323 } else if (event->atom == XCB_ATOM_WM_ICON_NAME) { 0324 dirtyProperties |= NET::WMIconName; // support for old iconic name 0325 } 0326 } 0327 if (mapViewport() && (dirtyProperties & (NET::WMState | NET::WMGeometry))) { 0328 /* geometry change -> possible viewport change 0329 * state change -> possible NET::Sticky change 0330 */ 0331 dirtyProperties |= NET::WMDesktop; 0332 } 0333 if ((dirtyProperties & NET::WMStrut) != 0) { 0334 removeStrutWindow(eventWindow); 0335 if (!possibleStrutWindows.contains(eventWindow)) { 0336 possibleStrutWindows.append(eventWindow); 0337 } 0338 } 0339 if (dirtyProperties || dirtyProperties2) { 0340 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 80) 0341 Q_EMIT s_q->windowChanged(eventWindow); 0342 Q_EMIT s_q->windowChanged(eventWindow, dirtyProperties, dirtyProperties2); 0343 #endif 0344 Q_EMIT KX11Extras::self()->windowChanged(eventWindow, dirtyProperties, dirtyProperties2); 0345 0346 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 0) 0347 unsigned long dirty[2] = {dirtyProperties, dirtyProperties2}; 0348 Q_EMIT s_q->windowChanged(eventWindow, dirty); 0349 Q_EMIT s_q->windowChanged(eventWindow, dirtyProperties); 0350 #endif 0351 if ((dirtyProperties & NET::WMStrut) != 0) { 0352 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0353 Q_EMIT s_q->strutChanged(); 0354 #endif 0355 Q_EMIT KX11Extras::self()->strutChanged(); 0356 } 0357 } 0358 } 0359 0360 return false; 0361 } 0362 0363 bool NETEventFilter::removeStrutWindow(WId w) 0364 { 0365 for (QList<StrutData>::Iterator it = strutWindows.begin(); it != strutWindows.end(); ++it) { 0366 if ((*it).window == w) { 0367 strutWindows.erase(it); 0368 return true; 0369 } 0370 } 0371 return false; 0372 } 0373 0374 void NETEventFilter::updateStackingOrder() 0375 { 0376 stackingOrder.clear(); 0377 for (int i = 0; i < clientListStackingCount(); i++) { 0378 stackingOrder.append(clientListStacking()[i]); 0379 } 0380 } 0381 0382 void NETEventFilter::addClient(xcb_window_t w) 0383 { 0384 KWindowSystem *s_q = KWindowSystem::self(); 0385 0386 if ((what >= KWindowSystemPrivateX11::INFO_WINDOWS)) { 0387 xcb_connection_t *c = QX11Info::connection(); 0388 UniqueCPointer<xcb_get_window_attributes_reply_t> attr(xcb_get_window_attributes_reply(c, xcb_get_window_attributes_unchecked(c, w), nullptr)); 0389 0390 uint32_t events = XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY; 0391 if (attr) { 0392 events = events | attr->your_event_mask; 0393 } 0394 xcb_change_window_attributes(c, w, XCB_CW_EVENT_MASK, &events); 0395 } 0396 0397 bool emit_strutChanged = false; 0398 0399 if (strutSignalConnected) { 0400 NETWinInfo info(QX11Info::connection(), w, QX11Info::appRootWindow(), NET::WMStrut | NET::WMDesktop, NET::Properties2()); 0401 NETStrut strut = info.strut(); 0402 if (strut.left || strut.top || strut.right || strut.bottom) { 0403 strutWindows.append(StrutData(w, strut, info.desktop())); 0404 emit_strutChanged = true; 0405 } 0406 } else { 0407 possibleStrutWindows.append(w); 0408 } 0409 0410 windows.append(w); 0411 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0412 Q_EMIT s_q->windowAdded(w); 0413 #endif 0414 Q_EMIT KX11Extras::self()->windowAdded(w); 0415 if (emit_strutChanged) { 0416 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0417 Q_EMIT s_q->strutChanged(); 0418 #endif 0419 Q_EMIT KX11Extras::self()->strutChanged(); 0420 } 0421 } 0422 0423 void NETEventFilter::removeClient(xcb_window_t w) 0424 { 0425 KWindowSystem *s_q = KWindowSystem::self(); 0426 0427 bool emit_strutChanged = removeStrutWindow(w); 0428 if (strutSignalConnected && possibleStrutWindows.contains(w)) { 0429 NETWinInfo info(QX11Info::connection(), w, QX11Info::appRootWindow(), NET::WMStrut, NET::Properties2()); 0430 NETStrut strut = info.strut(); 0431 if (strut.left || strut.top || strut.right || strut.bottom) { 0432 emit_strutChanged = true; 0433 } 0434 } 0435 0436 possibleStrutWindows.removeAll(w); 0437 windows.removeAll(w); 0438 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0439 Q_EMIT s_q->windowRemoved(w); 0440 #endif 0441 Q_EMIT KX11Extras::self()->windowRemoved(w); 0442 if (emit_strutChanged) { 0443 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0444 Q_EMIT s_q->strutChanged(); 0445 #endif 0446 Q_EMIT KX11Extras::self()->strutChanged(); 0447 } 0448 } 0449 0450 bool NETEventFilter::mapViewport() 0451 { 0452 // compiz claims support even though it doesn't use virtual desktops :( 0453 // if( isSupported( NET::DesktopViewport ) && !isSupported( NET::NumberOfDesktops )) 0454 0455 // this test is duplicated in KWindowSystem::mapViewport() 0456 if (isSupported(NET::DesktopViewport) && numberOfDesktops(true) <= 1 0457 && (desktopGeometry().width > displayWidth() || desktopGeometry().height > displayHeight())) { 0458 return true; 0459 } 0460 return false; 0461 } 0462 0463 static bool atoms_created = false; 0464 0465 static Atom _wm_protocols; 0466 static Atom _wm_change_state; 0467 static Atom kwm_utf8_string; 0468 0469 static void create_atoms() 0470 { 0471 if (!atoms_created) { 0472 const int max = 20; 0473 Atom *atoms[max]; 0474 const char *names[max]; 0475 Atom atoms_return[max]; 0476 int n = 0; 0477 0478 atoms[n] = &_wm_protocols; 0479 names[n++] = "WM_PROTOCOLS"; 0480 0481 atoms[n] = &_wm_change_state; 0482 names[n++] = "WM_CHANGE_STATE"; 0483 0484 atoms[n] = &kwm_utf8_string; 0485 names[n++] = "UTF8_STRING"; 0486 0487 char net_wm_cm_name[100]; 0488 sprintf(net_wm_cm_name, "_NET_WM_CM_S%d", QX11Info::appScreen()); 0489 atoms[n] = &net_wm_cm; 0490 names[n++] = net_wm_cm_name; 0491 0492 // we need a const_cast for the shitty X API 0493 XInternAtoms(QX11Info::display(), const_cast<char **>(names), n, false, atoms_return); 0494 for (int i = 0; i < n; i++) { 0495 *atoms[i] = atoms_return[i]; 0496 } 0497 0498 atoms_created = True; 0499 } 0500 } 0501 0502 // optimalization - create KWindowSystemPrivate only when needed and only for what is needed 0503 void KWindowSystemPrivateX11::connectNotify(const QMetaMethod &signal) 0504 { 0505 FilterInfo what = INFO_BASIC; 0506 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0507 if (signal == QMetaMethod::fromSignal(&KWindowSystem::workAreaChanged)) { 0508 what = INFO_WINDOWS; 0509 } else if (signal == QMetaMethod::fromSignal(&KWindowSystem::strutChanged)) { 0510 what = INFO_WINDOWS; 0511 } else if (signal == QMetaMethod::fromSignal(static_cast<void (KWindowSystem::*)(WId, NET::Properties, NET::Properties2)>(&KWindowSystem::windowChanged))) { 0512 what = INFO_WINDOWS; 0513 } 0514 #endif 0515 if (signal == QMetaMethod::fromSignal(&KX11Extras::workAreaChanged)) { 0516 what = INFO_WINDOWS; 0517 } else if (signal == QMetaMethod::fromSignal(&KX11Extras::strutChanged)) { 0518 what = INFO_WINDOWS; 0519 } else if (signal == QMetaMethod::fromSignal(&KX11Extras::windowChanged)) { 0520 what = INFO_WINDOWS; 0521 } 0522 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 0) 0523 else if (signal == QMetaMethod::fromSignal(static_cast<void (KWindowSystem::*)(WId, const unsigned long *)>(&KWindowSystem::windowChanged))) { 0524 what = INFO_WINDOWS; 0525 } else if (signal == QMetaMethod::fromSignal(static_cast<void (KWindowSystem::*)(WId, uint)>(&KWindowSystem::windowChanged))) { 0526 what = INFO_WINDOWS; 0527 } 0528 #endif 0529 0530 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 80) 0531 else if (signal == QMetaMethod::fromSignal(static_cast<void (KWindowSystem::*)(WId)>(&KWindowSystem::windowChanged))) { 0532 what = INFO_WINDOWS; 0533 } 0534 #endif 0535 0536 init(what); 0537 NETEventFilter *const s_d = s_d_func(); 0538 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0539 if (!s_d->strutSignalConnected && signal == QMetaMethod::fromSignal(&KWindowSystem::strutChanged)) { 0540 s_d->strutSignalConnected = true; 0541 } 0542 #endif 0543 if (!s_d->strutSignalConnected && signal == QMetaMethod::fromSignal(&KX11Extras::strutChanged)) { 0544 s_d->strutSignalConnected = true; 0545 } 0546 } 0547 0548 // WARNING 0549 // you have to call s_d_func() again after calling this function if you want a valid pointer! 0550 void KWindowSystemPrivateX11::init(FilterInfo what) 0551 { 0552 NETEventFilter *const s_d = s_d_func(); 0553 0554 if (what >= INFO_WINDOWS) { 0555 what = INFO_WINDOWS; 0556 } else { 0557 what = INFO_BASIC; 0558 } 0559 0560 if (!s_d || s_d->what < what) { 0561 const bool wasCompositing = s_d ? s_d->compositingEnabled : false; 0562 MainThreadInstantiator instantiator(what); 0563 NETEventFilter *filter; 0564 if (instantiator.thread() == QCoreApplication::instance()->thread()) { 0565 filter = instantiator.createNETEventFilter(); 0566 } else { 0567 // the instantiator is not in the main app thread, which implies 0568 // we are being called in a thread that is not the main app thread 0569 // so we move the instantiator to the main app thread and invoke 0570 // the method with a blocking call 0571 instantiator.moveToThread(QCoreApplication::instance()->thread()); 0572 QMetaObject::invokeMethod(&instantiator, "createNETEventFilter", Qt::BlockingQueuedConnection, Q_RETURN_ARG(NETEventFilter *, filter)); 0573 } 0574 d.reset(filter); 0575 d->activate(); 0576 if (wasCompositing != s_d_func()->compositingEnabled) { 0577 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0578 Q_EMIT KWindowSystem::self()->compositingChanged(s_d_func()->compositingEnabled); 0579 #endif 0580 Q_EMIT KX11Extras::self()->compositingChanged(s_d_func()->compositingEnabled); 0581 } 0582 } 0583 } 0584 0585 QList<WId> KWindowSystemPrivateX11::windows() 0586 { 0587 init(INFO_BASIC); 0588 return s_d_func()->windows; 0589 } 0590 0591 QList<WId> KWindowSystemPrivateX11::stackingOrder() 0592 { 0593 init(INFO_BASIC); 0594 return s_d_func()->stackingOrder; 0595 } 0596 0597 int KWindowSystemPrivateX11::currentDesktop() 0598 { 0599 if (!QX11Info::connection()) { 0600 return 1; 0601 } 0602 0603 if (mapViewport()) { 0604 init(INFO_BASIC); 0605 NETEventFilter *const s_d = s_d_func(); 0606 NETPoint p = s_d->desktopViewport(s_d->currentDesktop(true)); 0607 return viewportToDesktop(QPoint(p.x, p.y)); 0608 } 0609 0610 NETEventFilter *const s_d = s_d_func(); 0611 if (s_d) { 0612 return s_d->currentDesktop(true); 0613 } 0614 NETRootInfo info(QX11Info::connection(), NET::CurrentDesktop, NET::Properties2(), QX11Info::appScreen()); 0615 return info.currentDesktop(true); 0616 } 0617 0618 int KWindowSystemPrivateX11::numberOfDesktops() 0619 { 0620 if (!QX11Info::connection()) { 0621 return 1; 0622 } 0623 0624 if (mapViewport()) { 0625 init(INFO_BASIC); 0626 NETEventFilter *const s_d = s_d_func(); 0627 NETSize s = s_d->desktopGeometry(); 0628 return s.width / displayWidth() * s.height / displayHeight(); 0629 } 0630 0631 NETEventFilter *const s_d = s_d_func(); 0632 if (s_d) { 0633 return s_d->numberOfDesktops(true); 0634 } 0635 NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops, NET::Properties2(), QX11Info::appScreen()); 0636 return info.numberOfDesktops(true); 0637 } 0638 0639 void KWindowSystemPrivateX11::setCurrentDesktop(int desktop) 0640 { 0641 if (mapViewport()) { 0642 init(INFO_BASIC); 0643 NETEventFilter *const s_d = s_d_func(); 0644 NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::Properties2(), QX11Info::appScreen()); 0645 QPoint pos = desktopToViewport(desktop, true); 0646 NETPoint p; 0647 p.x = pos.x(); 0648 p.y = pos.y(); 0649 info.setDesktopViewport(s_d->currentDesktop(true), p); 0650 return; 0651 } 0652 NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::Properties2(), QX11Info::appScreen()); 0653 info.setCurrentDesktop(desktop, true); 0654 } 0655 0656 void KWindowSystemPrivateX11::setOnAllDesktops(WId win, bool b) 0657 { 0658 if (mapViewport()) { 0659 if (b) { 0660 setState(win, NET::Sticky); 0661 } else { 0662 clearState(win, NET::Sticky); 0663 } 0664 return; 0665 } 0666 NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::WMDesktop, NET::Properties2()); 0667 if (b) { 0668 info.setDesktop(NETWinInfo::OnAllDesktops, true); 0669 } else if (info.desktop(true) == NETWinInfo::OnAllDesktops) { 0670 NETRootInfo rinfo(QX11Info::connection(), NET::CurrentDesktop, NET::Properties2(), QX11Info::appScreen()); 0671 info.setDesktop(rinfo.currentDesktop(true), true); 0672 } 0673 } 0674 0675 void KWindowSystemPrivateX11::setOnDesktop(WId win, int desktop) 0676 { 0677 if (mapViewport()) { 0678 if (desktop == NET::OnAllDesktops) { 0679 return setOnAllDesktops(win, true); 0680 } else { 0681 clearState(win, NET::Sticky); 0682 } 0683 init(INFO_BASIC); 0684 QPoint p = desktopToViewport(desktop, false); 0685 Window dummy; 0686 int x; 0687 int y; 0688 unsigned int w; 0689 unsigned int h; 0690 unsigned int b; 0691 unsigned int dp; 0692 XGetGeometry(QX11Info::display(), win, &dummy, &x, &y, &w, &h, &b, &dp); 0693 // get global position 0694 XTranslateCoordinates(QX11Info::display(), win, QX11Info::appRootWindow(), 0, 0, &x, &y, &dummy); 0695 x += w / 2; // center 0696 y += h / 2; 0697 // transform to coordinates on the current "desktop" 0698 x = x % displayWidth(); 0699 y = y % displayHeight(); 0700 if (x < 0) { 0701 x = x + displayWidth(); 0702 } 0703 if (y < 0) { 0704 y = y + displayHeight(); 0705 } 0706 x += p.x(); // move to given "desktop" 0707 y += p.y(); 0708 x -= w / 2; // from center back to topleft 0709 y -= h / 2; 0710 p = constrainViewportRelativePosition(QPoint(x, y)); 0711 int flags = (NET::FromTool << 12) | (0x03 << 8) | 10; // from tool(?), x/y, static gravity 0712 NETEventFilter *const s_d = s_d_func(); 0713 s_d->moveResizeWindowRequest(win, flags, p.x(), p.y(), w, h); 0714 return; 0715 } 0716 NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::WMDesktop, NET::Properties2()); 0717 info.setDesktop(desktop, true); 0718 } 0719 0720 void KWindowSystemPrivateX11::setOnActivities(WId win, const QStringList &activities) 0721 { 0722 NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::Properties(), NET::WM2Activities); 0723 info.setActivities(activities.join(QLatin1Char(',')).toLatin1().constData()); 0724 } 0725 0726 WId KWindowSystemPrivateX11::activeWindow() 0727 { 0728 NETEventFilter *const s_d = s_d_func(); 0729 if (s_d) { 0730 return s_d->activeWindow(); 0731 } 0732 NETRootInfo info(QX11Info::connection(), NET::ActiveWindow, NET::Properties2(), QX11Info::appScreen()); 0733 return info.activeWindow(); 0734 } 0735 0736 void KWindowSystemPrivateX11::activateWindow(WId win, long time) 0737 { 0738 NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::Properties2(), QX11Info::appScreen()); 0739 if (time == 0) { 0740 time = QX11Info::appUserTime(); 0741 } 0742 info.setActiveWindow(win, NET::FromApplication, time, QGuiApplication::focusWindow() ? QGuiApplication::focusWindow()->winId() : 0); 0743 } 0744 0745 void KWindowSystemPrivateX11::forceActiveWindow(WId win, long time) 0746 { 0747 NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::Properties2(), QX11Info::appScreen()); 0748 if (time == 0) { 0749 time = QX11Info::appTime(); 0750 } 0751 info.setActiveWindow(win, NET::FromTool, time, 0); 0752 } 0753 0754 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0755 void KWindowSystemPrivateX11::demandAttention(WId win, bool set) 0756 { 0757 NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::WMState, NET::Properties2()); 0758 info.setState(set ? NET::DemandsAttention : NET::States(), NET::DemandsAttention); 0759 } 0760 #endif 0761 0762 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 0) 0763 WId KWindowSystemPrivateX11::transientFor(WId win) 0764 { 0765 KWindowInfo info(win, NET::Properties(), NET::WM2TransientFor); 0766 return info.transientFor(); 0767 } 0768 0769 WId KWindowSystemPrivateX11::groupLeader(WId win) 0770 { 0771 KWindowInfo info(win, NET::Properties(), NET::WM2GroupLeader); 0772 return info.groupLeader(); 0773 } 0774 #endif 0775 0776 QPixmap KWindowSystemPrivateX11::icon(WId win, int width, int height, bool scale, int flags) 0777 { 0778 NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::WMIcon, NET::WM2WindowClass | NET::WM2IconPixmap); 0779 return iconFromNetWinInfo(width, height, scale, flags, &info); 0780 } 0781 0782 QPixmap KWindowSystemPrivateX11::iconFromNetWinInfo(int width, int height, bool scale, int flags, NETWinInfo *info) 0783 { 0784 QPixmap result; 0785 if (!info) { 0786 return result; 0787 } 0788 if (flags & KX11Extras::NETWM) { 0789 NETIcon ni = info->icon(width, height); 0790 if (ni.data && ni.size.width > 0 && ni.size.height > 0) { 0791 QImage img((uchar *)ni.data, (int)ni.size.width, (int)ni.size.height, QImage::Format_ARGB32); 0792 if (scale && width > 0 && height > 0 && img.size() != QSize(width, height) && !img.isNull()) { 0793 img = img.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0794 } 0795 if (!img.isNull()) { 0796 result = QPixmap::fromImage(img); 0797 } 0798 return result; 0799 } 0800 } 0801 0802 if (flags & KX11Extras::WMHints) { 0803 xcb_pixmap_t p = info->icccmIconPixmap(); 0804 xcb_pixmap_t p_mask = info->icccmIconPixmapMask(); 0805 0806 if (p != XCB_PIXMAP_NONE) { 0807 QPixmap pm = KXUtils::createPixmapFromHandle(info->xcbConnection(), p, p_mask); 0808 if (scale && width > 0 && height > 0 && !pm.isNull() // 0809 && (pm.width() != width || pm.height() != height)) { 0810 result = QPixmap::fromImage(pm.toImage().scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); 0811 } else { 0812 result = pm; 0813 } 0814 } 0815 } 0816 0817 // Since width can be any arbitrary size, but the icons cannot, 0818 // take the nearest value for best results (ignoring 22 pixel 0819 // icons as they don't exist for apps): 0820 int iconWidth; 0821 if (width < 24) { 0822 iconWidth = 16; 0823 } else if (width < 40) { 0824 iconWidth = 32; 0825 } else if (width < 56) { 0826 iconWidth = 48; 0827 } else if (width < 96) { 0828 iconWidth = 64; 0829 } else if (width < 192) { 0830 iconWidth = 128; 0831 } else { 0832 iconWidth = 256; 0833 } 0834 0835 if (flags & KX11Extras::ClassHint) { 0836 // Try to load the icon from the classhint if the app didn't specify 0837 // its own: 0838 if (result.isNull()) { 0839 const QIcon icon = QIcon::fromTheme(QString::fromUtf8(info->windowClassClass()).toLower()); 0840 const QPixmap pm = icon.isNull() ? QPixmap() : icon.pixmap(iconWidth, iconWidth); 0841 if (scale && !pm.isNull()) { 0842 result = QPixmap::fromImage(pm.toImage().scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); 0843 } else { 0844 result = pm; 0845 } 0846 } 0847 } 0848 0849 if (flags & KX11Extras::XApp) { 0850 // If the icon is still a null pixmap, load the icon for X applications 0851 // as a last resort: 0852 if (result.isNull()) { 0853 const QIcon icon = QIcon::fromTheme(QStringLiteral("xorg")); 0854 const QPixmap pm = icon.isNull() ? QPixmap() : icon.pixmap(iconWidth, iconWidth); 0855 if (scale && !pm.isNull()) { 0856 result = QPixmap::fromImage(pm.toImage().scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); 0857 } else { 0858 result = pm; 0859 } 0860 } 0861 } 0862 return result; 0863 } 0864 0865 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 101) 0866 void KWindowSystemPrivateX11::setIcons(WId win, const QPixmap &icon, const QPixmap &miniIcon) 0867 { 0868 if (icon.isNull()) { 0869 return; 0870 } 0871 NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::Properties(), NET::Properties2()); 0872 QImage img = icon.toImage().convertToFormat(QImage::Format_ARGB32); 0873 NETIcon ni; 0874 ni.size.width = img.size().width(); 0875 ni.size.height = img.size().height(); 0876 ni.data = (unsigned char *)img.bits(); 0877 info.setIcon(ni, true); 0878 if (miniIcon.isNull()) { 0879 return; 0880 } 0881 img = miniIcon.toImage().convertToFormat(QImage::Format_ARGB32); 0882 if (img.isNull()) { 0883 return; 0884 } 0885 ni.size.width = img.size().width(); 0886 ni.size.height = img.size().height(); 0887 ni.data = (unsigned char *)img.bits(); 0888 info.setIcon(ni, false); 0889 } 0890 #endif 0891 0892 void KWindowSystemPrivateX11::setType(WId win, NET::WindowType windowType) 0893 { 0894 NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::Properties(), NET::Properties2()); 0895 info.setWindowType(windowType); 0896 } 0897 0898 void KWindowSystemPrivateX11::setState(WId win, NET::States state) 0899 { 0900 NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::WMState, NET::Properties2()); 0901 info.setState(state, state); 0902 } 0903 0904 void KWindowSystemPrivateX11::clearState(WId win, NET::States state) 0905 { 0906 NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::WMState, NET::Properties2()); 0907 info.setState(NET::States(), state); 0908 } 0909 0910 // enum values for ICCCM 4.1.2.4 and 4.1.4, defined to not depend on xcb-icccm 0911 enum { 0912 _ICCCM_WM_STATE_WITHDRAWN = 0, 0913 _ICCCM_WM_STATE_NORMAL = 1, 0914 _ICCCM_WM_STATE_ICONIC = 3, 0915 }; 0916 0917 void KWindowSystemPrivateX11::minimizeWindow(WId win) 0918 { 0919 create_atoms(); 0920 // as described in ICCCM 4.1.4 0921 xcb_client_message_event_t ev; 0922 memset(&ev, 0, sizeof(ev)); 0923 ev.response_type = XCB_CLIENT_MESSAGE; 0924 ev.window = win; 0925 ev.type = _wm_change_state; 0926 ev.format = 32; 0927 ev.data.data32[0] = _ICCCM_WM_STATE_ICONIC; 0928 ev.data.data32[1] = 0; 0929 ev.data.data32[2] = 0; 0930 ev.data.data32[3] = 0; 0931 ev.data.data32[4] = 0; 0932 xcb_send_event(QX11Info::connection(), 0933 false, 0934 QX11Info::appRootWindow(), 0935 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, 0936 reinterpret_cast<const char *>(&ev)); 0937 } 0938 0939 void KWindowSystemPrivateX11::unminimizeWindow(WId win) 0940 { 0941 xcb_map_window(QX11Info::connection(), win); 0942 } 0943 0944 void KWindowSystemPrivateX11::raiseWindow(WId win) 0945 { 0946 NETRootInfo info(QX11Info::connection(), NET::Supported, NET::Properties2(), QX11Info::appScreen()); 0947 if (info.isSupported(NET::WM2RestackWindow)) { 0948 info.restackRequest(win, NET::FromTool, XCB_WINDOW_NONE, XCB_STACK_MODE_ABOVE, QX11Info::appUserTime()); 0949 } else { 0950 const uint32_t values[] = {XCB_STACK_MODE_ABOVE}; 0951 xcb_configure_window(QX11Info::connection(), win, XCB_CONFIG_WINDOW_STACK_MODE, values); 0952 } 0953 } 0954 0955 void KWindowSystemPrivateX11::lowerWindow(WId win) 0956 { 0957 NETRootInfo info(QX11Info::connection(), NET::Supported, NET::Properties2(), QX11Info::appScreen()); 0958 if (info.isSupported(NET::WM2RestackWindow)) { 0959 info.restackRequest(win, NET::FromTool, XCB_WINDOW_NONE, XCB_STACK_MODE_BELOW, QX11Info::appUserTime()); 0960 } else { 0961 const uint32_t values[] = {XCB_STACK_MODE_BELOW}; 0962 xcb_configure_window(QX11Info::connection(), win, XCB_CONFIG_WINDOW_STACK_MODE, values); 0963 } 0964 } 0965 0966 bool KWindowSystemPrivateX11::compositingActive() 0967 { 0968 init(INFO_BASIC); 0969 if (s_d_func()->haveXfixes) { 0970 return s_d_func()->compositingEnabled; 0971 } else { 0972 create_atoms(); 0973 return XGetSelectionOwner(QX11Info::display(), net_wm_cm); 0974 } 0975 } 0976 0977 QRect KWindowSystemPrivateX11::workArea(int desktop) 0978 { 0979 init(INFO_BASIC); 0980 int desk = (desktop > 0 && desktop <= (int)s_d_func()->numberOfDesktops()) ? desktop : currentDesktop(); 0981 if (desk <= 0) { 0982 return displayGeometry(); 0983 } 0984 0985 NETRect r = s_d_func()->workArea(desk); 0986 if (r.size.width <= 0 || r.size.height <= 0) { // not set 0987 return displayGeometry(); 0988 } 0989 0990 return QRect(r.pos.x, r.pos.y, r.size.width, r.size.height); 0991 } 0992 0993 QRect KWindowSystemPrivateX11::workArea(const QList<WId> &exclude, int desktop) 0994 { 0995 init(INFO_WINDOWS); // invalidates s_d_func's return value 0996 NETEventFilter *const s_d = s_d_func(); 0997 0998 QRect all = displayGeometry(); 0999 QRect a = all; 1000 1001 if (desktop == -1) { 1002 desktop = s_d->currentDesktop(); 1003 } 1004 1005 QList<WId>::ConstIterator it1; 1006 for (it1 = s_d->windows.constBegin(); it1 != s_d->windows.constEnd(); ++it1) { 1007 if (exclude.contains(*it1)) { 1008 continue; 1009 } 1010 1011 // Kicker (very) extensively calls this function, causing hundreds of roundtrips just 1012 // to repeatedly find out struts of all windows. Therefore strut values for strut 1013 // windows are cached here. 1014 NETStrut strut; 1015 auto it2 = s_d->strutWindows.begin(); 1016 for (; it2 != s_d->strutWindows.end(); ++it2) { 1017 if ((*it2).window == *it1) { 1018 break; 1019 } 1020 } 1021 1022 if (it2 != s_d->strutWindows.end()) { 1023 if (!((*it2).desktop == desktop || (*it2).desktop == NETWinInfo::OnAllDesktops)) { 1024 continue; 1025 } 1026 1027 strut = (*it2).strut; 1028 } else if (s_d->possibleStrutWindows.contains(*it1)) { 1029 NETWinInfo info(QX11Info::connection(), (*it1), QX11Info::appRootWindow(), NET::WMStrut | NET::WMDesktop, NET::Properties2()); 1030 strut = info.strut(); 1031 s_d->possibleStrutWindows.removeAll(*it1); 1032 s_d->strutWindows.append(NETEventFilter::StrutData(*it1, info.strut(), info.desktop())); 1033 1034 if (!(info.desktop() == desktop || info.desktop() == NETWinInfo::OnAllDesktops)) { 1035 continue; 1036 } 1037 } else { 1038 continue; // not a strut window 1039 } 1040 1041 QRect r = all; 1042 if (strut.left > 0) { 1043 r.setLeft(r.left() + (int)strut.left); 1044 } 1045 if (strut.top > 0) { 1046 r.setTop(r.top() + (int)strut.top); 1047 } 1048 if (strut.right > 0) { 1049 r.setRight(r.right() - (int)strut.right); 1050 } 1051 if (strut.bottom > 0) { 1052 r.setBottom(r.bottom() - (int)strut.bottom); 1053 } 1054 1055 a = a.intersected(r); 1056 } 1057 return a; 1058 } 1059 1060 QString KWindowSystemPrivateX11::desktopName(int desktop) 1061 { 1062 init(INFO_BASIC); 1063 NETEventFilter *const s_d = s_d_func(); 1064 1065 bool isDesktopSane = (desktop > 0 && desktop <= (int)s_d->numberOfDesktops()); 1066 const char *name = s_d->desktopName(isDesktopSane ? desktop : currentDesktop()); 1067 1068 if (name && name[0]) { 1069 return QString::fromUtf8(name); 1070 } 1071 1072 return KWindowSystem::tr("Desktop %1").arg(desktop); 1073 } 1074 1075 void KWindowSystemPrivateX11::setDesktopName(int desktop, const QString &name) 1076 { 1077 NETEventFilter *const s_d = s_d_func(); 1078 1079 if (desktop <= 0 || desktop > (int)numberOfDesktops()) { 1080 desktop = currentDesktop(); 1081 } 1082 1083 if (s_d) { 1084 s_d->setDesktopName(desktop, name.toUtf8().constData()); 1085 return; 1086 } 1087 1088 NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::Properties2(), QX11Info::appScreen()); 1089 info.setDesktopName(desktop, name.toUtf8().constData()); 1090 } 1091 1092 bool KWindowSystemPrivateX11::showingDesktop() 1093 { 1094 init(INFO_BASIC); 1095 return s_d_func()->showingDesktop(); 1096 } 1097 1098 void KWindowSystemPrivateX11::setShowingDesktop(bool showing) 1099 { 1100 NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::WM2ShowingDesktop, QX11Info::appScreen()); 1101 info.setShowingDesktop(showing); 1102 } 1103 1104 void KWindowSystemPrivateX11::setUserTime(WId win, long time) 1105 { 1106 NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::Properties(), NET::Properties2()); 1107 info.setUserTime(time); 1108 } 1109 1110 void KWindowSystemPrivateX11::setExtendedStrut(WId win, 1111 int left_width, 1112 int left_start, 1113 int left_end, 1114 int right_width, 1115 int right_start, 1116 int right_end, 1117 int top_width, 1118 int top_start, 1119 int top_end, 1120 int bottom_width, 1121 int bottom_start, 1122 int bottom_end) 1123 { 1124 NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::Properties(), NET::Properties2()); 1125 NETExtendedStrut strut; 1126 strut.left_width = left_width; 1127 strut.right_width = right_width; 1128 strut.top_width = top_width; 1129 strut.bottom_width = bottom_width; 1130 strut.left_start = left_start; 1131 strut.left_end = left_end; 1132 strut.right_start = right_start; 1133 strut.right_end = right_end; 1134 strut.top_start = top_start; 1135 strut.top_end = top_end; 1136 strut.bottom_start = bottom_start; 1137 strut.bottom_end = bottom_end; 1138 info.setExtendedStrut(strut); 1139 NETStrut oldstrut; 1140 oldstrut.left = left_width; 1141 oldstrut.right = right_width; 1142 oldstrut.top = top_width; 1143 oldstrut.bottom = bottom_width; 1144 info.setStrut(oldstrut); 1145 } 1146 1147 void KWindowSystemPrivateX11::setStrut(WId win, int left, int right, int top, int bottom) 1148 { 1149 int w = displayWidth(); 1150 int h = displayHeight(); 1151 setExtendedStrut(win, left, 0, left != 0 ? w : 0, right, 0, right != 0 ? w : 0, top, 0, top != 0 ? h : 0, bottom, 0, bottom != 0 ? h : 0); 1152 } 1153 1154 bool KWindowSystemPrivateX11::icccmCompliantMappingState() 1155 { 1156 static enum { noidea, yes, no } wm_is_1_2_compliant = noidea; 1157 if (wm_is_1_2_compliant == noidea) { 1158 NETRootInfo info(QX11Info::connection(), NET::Supported, NET::Properties2(), QX11Info::appScreen()); 1159 wm_is_1_2_compliant = info.isSupported(NET::Hidden) ? yes : no; 1160 } 1161 return wm_is_1_2_compliant == yes; 1162 } 1163 1164 bool KWindowSystemPrivateX11::allowedActionsSupported() 1165 { 1166 static enum { noidea, yes, no } wm_supports_allowed_actions = noidea; 1167 if (wm_supports_allowed_actions == noidea) { 1168 NETRootInfo info(QX11Info::connection(), NET::Supported, NET::Properties2(), QX11Info::appScreen()); 1169 wm_supports_allowed_actions = info.isSupported(NET::WM2AllowedActions) ? yes : no; 1170 } 1171 return wm_supports_allowed_actions == yes; 1172 } 1173 1174 QString KWindowSystemPrivateX11::readNameProperty(WId win, unsigned long atom) 1175 { 1176 XTextProperty tp; 1177 char **text = nullptr; 1178 int count; 1179 QString result; 1180 if (XGetTextProperty(QX11Info::display(), win, &tp, atom) != 0 && tp.value != nullptr) { 1181 create_atoms(); 1182 1183 if (tp.encoding == kwm_utf8_string) { 1184 result = QString::fromUtf8((const char *)tp.value); 1185 } else if (XmbTextPropertyToTextList(QX11Info::display(), &tp, &text, &count) == Success && text != nullptr && count > 0) { 1186 result = QString::fromLocal8Bit(text[0]); 1187 } else if (tp.encoding == XA_STRING) { 1188 result = QString::fromLocal8Bit((const char *)tp.value); 1189 } 1190 if (text != nullptr) { 1191 XFreeStringList(text); 1192 } 1193 XFree(tp.value); 1194 } 1195 return result; 1196 } 1197 1198 void KWindowSystemPrivateX11::allowExternalProcessWindowActivation(int pid) 1199 { 1200 // Normally supported by X11, but may depend on some window managers ? 1201 Q_UNUSED(pid) 1202 } 1203 1204 void KWindowSystemPrivateX11::setBlockingCompositing(WId window, bool active) 1205 { 1206 NETWinInfo info(QX11Info::connection(), window, QX11Info::appRootWindow(), NET::Properties(), NET::Properties2()); 1207 info.setBlockingCompositing(active); 1208 } 1209 1210 bool KWindowSystemPrivateX11::mapViewport() 1211 { 1212 NETEventFilter *const s_d = s_d_func(); 1213 if (s_d) { 1214 return s_d->mapViewport(); 1215 } 1216 // avoid creating KWindowSystemPrivate 1217 NETRootInfo infos(QX11Info::connection(), NET::Supported, NET::Properties2(), QX11Info::appScreen()); 1218 if (!infos.isSupported(NET::DesktopViewport)) { 1219 return false; 1220 } 1221 NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops | NET::CurrentDesktop | NET::DesktopGeometry, NET::Properties2(), QX11Info::appScreen()); 1222 if (info.numberOfDesktops(true) <= 1 && (info.desktopGeometry().width > displayWidth() || info.desktopGeometry().height > displayHeight())) { 1223 return true; 1224 } 1225 return false; 1226 } 1227 1228 int KWindowSystemPrivateX11::viewportToDesktop(const QPoint &p) 1229 { 1230 init(INFO_BASIC); 1231 NETEventFilter *const s_d = s_d_func(); 1232 NETSize s = s_d->desktopGeometry(); 1233 QSize vs(displayWidth(), displayHeight()); 1234 int xs = s.width / vs.width(); 1235 int x = p.x() < 0 ? 0 : p.x() >= s.width ? xs - 1 : p.x() / vs.width(); 1236 int ys = s.height / vs.height(); 1237 int y = p.y() < 0 ? 0 : p.y() >= s.height ? ys - 1 : p.y() / vs.height(); 1238 return y * xs + x + 1; 1239 } 1240 1241 int KWindowSystemPrivateX11::viewportWindowToDesktop(const QRect &r) 1242 { 1243 init(INFO_BASIC); 1244 NETEventFilter *const s_d = s_d_func(); 1245 QPoint p = r.center(); 1246 // make absolute 1247 p = QPoint(p.x() + s_d->desktopViewport(s_d->currentDesktop(true)).x, p.y() + s_d->desktopViewport(s_d->currentDesktop(true)).y); 1248 NETSize s = s_d->desktopGeometry(); 1249 QSize vs(displayWidth(), displayHeight()); 1250 int xs = s.width / vs.width(); 1251 int x = p.x() < 0 ? 0 : p.x() >= s.width ? xs - 1 : p.x() / vs.width(); 1252 int ys = s.height / vs.height(); 1253 int y = p.y() < 0 ? 0 : p.y() >= s.height ? ys - 1 : p.y() / vs.height(); 1254 return y * xs + x + 1; 1255 } 1256 1257 QPoint KWindowSystemPrivateX11::desktopToViewport(int desktop, bool absolute) 1258 { 1259 init(INFO_BASIC); 1260 NETEventFilter *const s_d = s_d_func(); 1261 NETSize s = s_d->desktopGeometry(); 1262 QSize vs(displayWidth(), displayHeight()); 1263 int xs = s.width / vs.width(); 1264 int ys = s.height / vs.height(); 1265 if (desktop <= 0 || desktop > xs * ys) { 1266 return QPoint(0, 0); 1267 } 1268 --desktop; 1269 QPoint ret(vs.width() * (desktop % xs), vs.height() * (desktop / xs)); 1270 if (!absolute) { 1271 ret = QPoint(ret.x() - s_d->desktopViewport(s_d->currentDesktop(true)).x, ret.y() - s_d->desktopViewport(s_d->currentDesktop(true)).y); 1272 if (ret.x() >= s.width) { 1273 ret.setX(ret.x() - s.width); 1274 } 1275 if (ret.x() < 0) { 1276 ret.setX(ret.x() + s.width); 1277 } 1278 if (ret.y() >= s.height) { 1279 ret.setY(ret.y() - s.height); 1280 } 1281 if (ret.y() < 0) { 1282 ret.setY(ret.y() + s.height); 1283 } 1284 } 1285 return ret; 1286 } 1287 1288 QPoint KWindowSystemPrivateX11::constrainViewportRelativePosition(const QPoint &pos) 1289 { 1290 init(INFO_BASIC); 1291 NETEventFilter *const s_d = s_d_func(); 1292 NETSize s = s_d->desktopGeometry(); 1293 NETPoint c = s_d->desktopViewport(s_d->currentDesktop(true)); 1294 int x = (pos.x() + c.x) % s.width; 1295 int y = (pos.y() + c.y) % s.height; 1296 if (x < 0) { 1297 x += s.width; 1298 } 1299 if (y < 0) { 1300 y += s.height; 1301 } 1302 return QPoint(x - c.x, y - c.y); 1303 } 1304 1305 #include "moc_kwindowsystem_p_x11.cpp"