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